diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2e1ed38efe01d..ec6d4d9304b9a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -771,7 +771,7 @@ namespace ts { const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]); const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType; const numberOrBigIntType = getUnionType([numberType, bigintType]); - const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType]); + const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType, nullType, undefinedType]) as UnionType; const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t) : t); const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t); @@ -13207,6 +13207,30 @@ namespace ts { return true; } + /** + * Returns `true` if the intersection of the template literals and string literals is the empty set, eg `get${string}` & "setX", and should reduce to `never` + */ + function extractRedundantTemplateLiterals(types: Type[]): boolean { + let i = types.length; + const literals = filter(types, t => !!(t.flags & TypeFlags.StringLiteral)); + while (i > 0) { + i--; + const t = types[i]; + if (!(t.flags & TypeFlags.TemplateLiteral)) continue; + for (const t2 of literals) { + if (isTypeSubtypeOf(t2, t)) { + // eg, ``get${T}` & "getX"` is just `"getX"` + orderedRemoveItemAt(types, i); + break; + } + else if (isPatternLiteralType(t)) { + return true; + } + } + } + return false; + } + function extractIrreducible(types: Type[], flag: TypeFlags) { if (every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)))) { for (let i = 0; i < types.length; i++) { @@ -13355,7 +13379,12 @@ namespace ts { } } else { - result = createIntersectionType(typeSet, aliasSymbol, aliasTypeArguments); + if (includes & TypeFlags.TemplateLiteral && includes & TypeFlags.StringLiteral && extractRedundantTemplateLiterals(typeSet)) { + result = neverType; + } + else { + result = createIntersectionType(typeSet, aliasSymbol, aliasTypeArguments); + } } intersectionTypes.set(id, result); } @@ -13531,7 +13560,7 @@ namespace ts { function addSpans(texts: readonly string[], types: readonly Type[]): boolean { for (let i = 0; i < types.length; i++) { const t = types[i]; - if (t.flags & TypeFlags.Literal) { + if (t.flags & (TypeFlags.Literal | TypeFlags.Null | TypeFlags.Undefined)) { text += getTemplateStringForType(t) || ""; text += texts[i + 1]; } @@ -13540,7 +13569,7 @@ namespace ts { if (!addSpans((t).texts, (t).types)) return false; text += texts[i + 1]; } - else if (isGenericIndexType(t)) { + else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t)) { newTypes.push(t); newTexts.push(text); text = texts[i + 1]; @@ -13558,6 +13587,8 @@ namespace ts { type.flags & TypeFlags.NumberLiteral ? "" + (type).value : type.flags & TypeFlags.BigIntLiteral ? pseudoBigIntToString((type).value) : type.flags & TypeFlags.BooleanLiteral ? (type).intrinsicName : + type.flags & TypeFlags.Null ? "null" : + type.flags & TypeFlags.Undefined ? "undefined" : undefined; } @@ -13817,6 +13848,14 @@ namespace ts { accessNode; } + function isPatternLiteralPlaceholderType(type: Type) { + return templateConstraintType.types.indexOf(type) !== -1 || !!(type.flags & TypeFlags.Any); + } + + function isPatternLiteralType(type: Type) { + return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType); + } + function isGenericObjectType(type: Type): boolean { if (type.flags & TypeFlags.UnionOrIntersection) { if (!((type).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) { @@ -13836,7 +13875,7 @@ namespace ts { } return !!((type).objectFlags & ObjectFlags.IsGenericIndexType); } - return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)); + return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type); } function isThisTypeParameter(type: Type): boolean { @@ -14562,6 +14601,8 @@ namespace ts { return !!(type.flags & TypeFlags.Literal) && (type).freshType === type; } + function getLiteralType(value: string): StringLiteralType; + function getLiteralType(value: string | number | PseudoBigInt, enumId?: number, symbol?: Symbol): LiteralType; function getLiteralType(value: string | number | PseudoBigInt, enumId?: number, symbol?: Symbol) { // We store all literal types in a single map with keys of the form '#NNN' and '@SSS', // where NNN is the text representation of a numeric literal and SSS are the characters @@ -17346,6 +17387,15 @@ namespace ts { } } } + else if (target.flags & TypeFlags.TemplateLiteral && source.flags & TypeFlags.StringLiteral) { + if (isPatternLiteralType(target)) { + // match all non-`string` segments + const result = inferLiteralsFromTemplateLiteralType(source as StringLiteralType, target as TemplateLiteralType); + if (result && every(result, (r, i) => isStringLiteralTypeValueParsableAsType(r, (target as TemplateLiteralType).types[i]))) { + return Ternary.True; + } + } + } if (source.flags & TypeFlags.TypeVariable) { if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) { @@ -17386,8 +17436,15 @@ namespace ts { } } else if (source.flags & TypeFlags.TemplateLiteral) { + if (target.flags & TypeFlags.TemplateLiteral && + (source as TemplateLiteralType).texts.length === (target as TemplateLiteralType).texts.length && + (source as TemplateLiteralType).types.length === (target as TemplateLiteralType).types.length && + every((source as TemplateLiteralType).texts, (t, i) => t === (target as TemplateLiteralType).texts[i]) && + every((instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers)) as TemplateLiteralType).types, (t, i) => !!((target as TemplateLiteralType).types[i].flags & (TypeFlags.Any | TypeFlags.String)) || !!isRelatedTo(t, (target as TemplateLiteralType).types[i], /*reportErrors*/ false))) { + return Ternary.True; + } const constraint = getBaseConstraintOfType(source); - if (constraint && (result = isRelatedTo(constraint, target, reportErrors))) { + if (constraint && constraint !== source && (result = isRelatedTo(constraint, target, reportErrors))) { resetErrorInfo(saveErrorInfo); return result; } @@ -18308,12 +18365,12 @@ namespace ts { if (type.flags & TypeFlags.Instantiable) { const constraint = getConstraintOfType(type); - if (constraint) { + if (constraint && constraint !== type) { return typeCouldHaveTopLevelSingletonTypes(constraint); } } - return isUnitType(type); + return isUnitType(type) || !!(type.flags & TypeFlags.TemplateLiteral); } function getBestMatchingType(source: Type, target: UnionOrIntersectionType, isRelatedTo = compareTypesAssignable) { @@ -19683,6 +19740,63 @@ namespace ts { return !!(type.symbol && some(type.symbol.declarations, hasSkipDirectInferenceFlag)); } + function isValidBigIntString(s: string): boolean { + const scanner = createScanner(ScriptTarget.ESNext, /*skipTrivia*/ false); + let success = true; + scanner.setOnError(() => success = false); + scanner.setText(s + "n"); + let result = scanner.scan(); + if (result === SyntaxKind.MinusToken) { + result = scanner.scan(); + } + const flags = scanner.getTokenFlags(); + // validate that + // * scanning proceeded without error + // * a bigint can be scanned, and that when it is scanned, it is + // * the full length of the input string (so the scanner is one character beyond the augmented input length) + // * it does not contain a numeric seperator (the `BigInt` constructor does not accept a numeric seperator in its input) + return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator); + } + + function isStringLiteralTypeValueParsableAsType(s: StringLiteralType, target: Type): boolean { + if (target.flags & TypeFlags.Union) { + return !!forEachType(target, t => isStringLiteralTypeValueParsableAsType(s, t)); + } + switch (target) { + case stringType: return true; + case numberType: return s.value !== "" && isFinite(+(s.value)); + case bigintType: return s.value !== "" && isValidBigIntString(s.value); + // the next 4 should be handled in `getTemplateLiteralType`, as they are all exactly one value, but are here for completeness, just in case + // this function is ever used on types which don't come from template literal holes + case trueType: return s.value === "true"; + case falseType: return s.value === "false"; + case undefinedType: return s.value === "undefined"; + case nullType: return s.value === "null"; + default: return !!(target.flags & TypeFlags.Any); + } + } + + function inferLiteralsFromTemplateLiteralType(source: StringLiteralType, target: TemplateLiteralType): StringLiteralType[] | undefined { + const value = source.value; + const texts = target.texts; + const lastIndex = texts.length - 1; + const startText = texts[0]; + const endText = texts[lastIndex]; + if (!(value.startsWith(startText) && value.endsWith(endText))) return undefined; + const matches = []; + const str = value.slice(startText.length, value.length - endText.length); + let pos = 0; + for (let i = 1; i < lastIndex; i++) { + const delim = texts[i]; + const delimPos = delim.length > 0 ? str.indexOf(delim, pos) : pos < str.length ? pos + 1 : -1; + if (delimPos < 0) return undefined; + matches.push(getLiteralType(str.slice(pos, delimPos))); + pos = delimPos + delim.length; + } + matches.push(getLiteralType(str.slice(pos))); + return matches; + } + function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) { let bivariant = false; let propagationType: Type; @@ -20160,27 +20274,6 @@ namespace ts { } } - function inferLiteralsFromTemplateLiteralType(source: StringLiteralType, target: TemplateLiteralType): Type[] | undefined { - const value = source.value; - const texts = target.texts; - const lastIndex = texts.length - 1; - const startText = texts[0]; - const endText = texts[lastIndex]; - if (!(value.startsWith(startText) && value.endsWith(endText))) return undefined; - const matches = []; - const str = value.slice(startText.length, value.length - endText.length); - let pos = 0; - for (let i = 1; i < lastIndex; i++) { - const delim = texts[i]; - const delimPos = delim.length > 0 ? str.indexOf(delim, pos) : pos < str.length ? pos + 1 : -1; - if (delimPos < 0) return undefined; - matches.push(getLiteralType(str.slice(pos, delimPos))); - pos = delimPos + delim.length; - } - matches.push(getLiteralType(str.slice(pos))); - return matches; - } - function inferFromObjectTypes(source: Type, target: Type) { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( (source).target === (target).target || isArrayType(source) && isArrayType(target))) { @@ -31678,9 +31771,6 @@ namespace ts { checkSourceElement(span.type); const type = getTypeFromTypeNode(span.type); checkTypeAssignableTo(type, templateConstraintType, span.type); - if (!everyType(type, t => !!(t.flags & TypeFlags.Literal) || isGenericIndexType(t))) { - error(span.type, Diagnostics.Template_literal_type_argument_0_is_not_literal_type_or_a_generic_type, typeToString(type)); - } } getTypeFromTypeNode(node); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 9e53407c4373c..f2ea2fc68d6d5 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3039,10 +3039,7 @@ "category": "Error", "code": 2792 }, - "Template literal type argument '{0}' is not literal type or a generic type.": { - "category": "Error", - "code": 2793 - }, + "Expected {0} arguments, but got {1}. Did you forget to include 'void' in your type argument to 'Promise'?": { "category": "Error", "code": 2794 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fbf308707a10d..8d93603731a63 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4925,7 +4925,7 @@ namespace ts { NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable, // The following flags are aggregated during union and intersection type construction /* @internal */ - IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive, + IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral, // The following flags are used for different purposes during union and intersection type construction /* @internal */ IncludesStructuredOrInstantiable = TypeParameter, diff --git a/tests/baselines/reference/constAssertions.js b/tests/baselines/reference/constAssertions.js index 238d436adaf5f..5e3b2b8b1875d 100644 --- a/tests/baselines/reference/constAssertions.js +++ b/tests/baselines/reference/constAssertions.js @@ -303,7 +303,7 @@ declare function ff2(x: T, y: U): `${T}-${U} declare const ts1: "foo-bar"; declare const ts2: "foo-1" | "foo-0"; declare const ts3: "top-left" | "top-right" | "bottom-left" | "bottom-right"; -declare function ff3(x: 'foo' | 'bar', y: object): string; +declare function ff3(x: 'foo' | 'bar', y: object): `foo${string}` | `bar${string}`; declare type Action = "verify" | "write"; declare type ContentMatch = "match" | "nonMatch"; declare type Outcome = `${Action}_${ContentMatch}`; diff --git a/tests/baselines/reference/constAssertions.types b/tests/baselines/reference/constAssertions.types index cdbebebd392aa..15206af8c0e59 100644 --- a/tests/baselines/reference/constAssertions.types +++ b/tests/baselines/reference/constAssertions.types @@ -441,13 +441,13 @@ const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right'); >'right' : "right" function ff3(x: 'foo' | 'bar', y: object) { ->ff3 : (x: 'foo' | 'bar', y: object) => string +>ff3 : (x: 'foo' | 'bar', y: object) => `foo${string}` | `bar${string}` >x : "foo" | "bar" >y : object return `${x}${y}` as const; ->`${x}${y}` as const : string ->`${x}${y}` : string +>`${x}${y}` as const : `foo${string}` | `bar${string}` +>`${x}${y}` : `foo${string}` | `bar${string}` >x : "foo" | "bar" >y : object } diff --git a/tests/baselines/reference/templateLiteralTypesPatterns.errors.txt b/tests/baselines/reference/templateLiteralTypesPatterns.errors.txt new file mode 100644 index 0000000000000..de5efa3fe1314 --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypesPatterns.errors.txt @@ -0,0 +1,335 @@ +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(7,7): error TS2322: Type '"no slash"' is not assignable to type '`/${string}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(14,10): error TS2345: Argument of type '"example.com/noprotocol"' is not assignable to parameter of type '`http://${string}` | `https://${string}` | `ftp://${string}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(16,10): error TS2345: Argument of type '"gopher://example.com/protocol"' is not assignable to parameter of type '`http://${string}` | `https://${string}` | `ftp://${string}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(26,7): error TS2345: Argument of type '"other"' is not assignable to parameter of type '"false" | "true"'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(35,11): error TS2345: Argument of type '"0"' is not assignable to parameter of type '"undefined" | "null"'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(36,11): error TS2345: Argument of type '"false"' is not assignable to parameter of type '"undefined" | "null"'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(37,11): error TS2345: Argument of type '"NaN"' is not assignable to parameter of type '"undefined" | "null"'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(38,11): error TS2345: Argument of type '""' is not assignable to parameter of type '"undefined" | "null"'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(39,11): error TS2345: Argument of type '"other"' is not assignable to parameter of type '"undefined" | "null"'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(60,9): error TS2345: Argument of type '"?"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(61,9): error TS2345: Argument of type '"NaN"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(62,9): error TS2345: Argument of type '"Infinity"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(63,9): error TS2345: Argument of type '"+Infinity"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(64,9): error TS2345: Argument of type '"-Infinity"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(65,9): error TS2345: Argument of type '"1_000"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(68,9): error TS2345: Argument of type '"a10"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(69,9): error TS2345: Argument of type '"10a"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(72,9): error TS2345: Argument of type '"- 1"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(73,9): error TS2345: Argument of type '"-/**/1"' is not assignable to parameter of type '`${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(85,9): error TS2345: Argument of type '"1e21"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(86,9): error TS2345: Argument of type '"1E21"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(87,9): error TS2345: Argument of type '"1e-21"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(88,9): error TS2345: Argument of type '"1E-21"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(91,9): error TS2345: Argument of type '"1.0"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(92,9): error TS2345: Argument of type '"1.1"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(93,9): error TS2345: Argument of type '"-1.1"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(94,9): error TS2345: Argument of type '"-1.1e-10"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(95,9): error TS2345: Argument of type '"-1.1E-10"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(96,9): error TS2345: Argument of type '"1.1e-10"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(99,9): error TS2345: Argument of type '"?"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(100,9): error TS2345: Argument of type '"NaN"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(101,9): error TS2345: Argument of type '"Infinity"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(102,9): error TS2345: Argument of type '"+Infinity"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(103,9): error TS2345: Argument of type '"-Infinity"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(104,9): error TS2345: Argument of type '"1_000"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(107,9): error TS2345: Argument of type '"- 1"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(108,9): error TS2345: Argument of type '"-/**/1"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(111,9): error TS2345: Argument of type '"a10n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(112,9): error TS2345: Argument of type '"10an"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(115,9): error TS2345: Argument of type '"1n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(116,9): error TS2345: Argument of type '"-1n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(117,9): error TS2345: Argument of type '"0n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(118,9): error TS2345: Argument of type '"0b1n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(119,9): error TS2345: Argument of type '"0x1n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(120,9): error TS2345: Argument of type '"0o1n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(121,9): error TS2345: Argument of type '"1e21n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(122,9): error TS2345: Argument of type '"1E21n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(123,9): error TS2345: Argument of type '"1e-21n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(124,9): error TS2345: Argument of type '"1E-21n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(125,9): error TS2345: Argument of type '"1.1n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(126,9): error TS2345: Argument of type '"-1.1n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(127,9): error TS2345: Argument of type '"-1.1e-10n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(128,9): error TS2345: Argument of type '"-1.1E-10n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(129,9): error TS2345: Argument of type '"1.1e-10n"' is not assignable to parameter of type '`${bigint}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(140,1): error TS2322: Type '`a${string}`' is not assignable to type '`a${number}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(141,1): error TS2322: Type '"bno"' is not assignable to type '`a${any}`'. +tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(160,7): error TS2322: Type '"anything"' is not assignable to type '`${number} ${number}`'. + + +==== tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts (57 errors) ==== + type RequiresLeadingSlash = `/${string}`; + + // ok + const a: RequiresLeadingSlash = "/bin"; + + // not ok + const b: RequiresLeadingSlash = "no slash"; + ~ +!!! error TS2322: Type '"no slash"' is not assignable to type '`/${string}`'. + + type Protocol = `${T}://${U}`; + function download(hostSpec: Protocol<"http" | "https" | "ftp", string>) { } + // ok, has protocol + download("http://example.com/protocol"); + // issues error - no protocol + download("example.com/noprotocol"); + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '"example.com/noprotocol"' is not assignable to parameter of type '`http://${string}` | `https://${string}` | `ftp://${string}`'. + // issues error, incorrect protocol + download("gopher://example.com/protocol"); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '"gopher://example.com/protocol"' is not assignable to parameter of type '`http://${string}` | `https://${string}` | `ftp://${string}`'. + + const q: RequiresLeadingSlash extends string ? true : false = true; + + declare function bools(x: `${boolean}`): void; + // ok + bools("true"); + bools("false"); + + // not ok + bools("other"); + ~~~~~~~ +!!! error TS2345: Argument of type '"other"' is not assignable to parameter of type '"false" | "true"'. + + type Pat = `${T}` + declare function nullishes(x: Pat): void; + // ok + nullishes("null"); + nullishes("undefined"); + + // not ok + nullishes("0"); + ~~~ +!!! error TS2345: Argument of type '"0"' is not assignable to parameter of type '"undefined" | "null"'. + nullishes("false"); + ~~~~~~~ +!!! error TS2345: Argument of type '"false"' is not assignable to parameter of type '"undefined" | "null"'. + nullishes("NaN"); + ~~~~~ +!!! error TS2345: Argument of type '"NaN"' is not assignable to parameter of type '"undefined" | "null"'. + nullishes(""); + ~~ +!!! error TS2345: Argument of type '""' is not assignable to parameter of type '"undefined" | "null"'. + nullishes("other"); + ~~~~~~~ +!!! error TS2345: Argument of type '"other"' is not assignable to parameter of type '"undefined" | "null"'. + + declare function numbers(x: `${number}`): void; + // the following should work + numbers("1"); + numbers("-1"); + numbers("0"); + numbers("0b1"); + numbers("0x1"); + numbers("0o1"); + numbers("1e21"); + numbers("1E21"); + numbers("1e-21"); + numbers("1E-21"); + numbers("1.1"); + numbers("-1.1"); + numbers("-1.1e-10"); + numbers("-1.1E-10"); + numbers("1.1e-10"); + + // the following should be errors since they're not numbers + numbers("?"); + ~~~ +!!! error TS2345: Argument of type '"?"' is not assignable to parameter of type '`${number}`'. + numbers("NaN"); + ~~~~~ +!!! error TS2345: Argument of type '"NaN"' is not assignable to parameter of type '`${number}`'. + numbers("Infinity"); + ~~~~~~~~~~ +!!! error TS2345: Argument of type '"Infinity"' is not assignable to parameter of type '`${number}`'. + numbers("+Infinity"); + ~~~~~~~~~~~ +!!! error TS2345: Argument of type '"+Infinity"' is not assignable to parameter of type '`${number}`'. + numbers("-Infinity"); + ~~~~~~~~~~~ +!!! error TS2345: Argument of type '"-Infinity"' is not assignable to parameter of type '`${number}`'. + numbers("1_000"); + ~~~~~~~ +!!! error TS2345: Argument of type '"1_000"' is not assignable to parameter of type '`${number}`'. + + // the following should be errors since they don't match the pattern + numbers("a10"); + ~~~~~ +!!! error TS2345: Argument of type '"a10"' is not assignable to parameter of type '`${number}`'. + numbers("10a"); + ~~~~~ +!!! error TS2345: Argument of type '"10a"' is not assignable to parameter of type '`${number}`'. + + // whitespace and comments aren't part of numbers + numbers("- 1"); + ~~~~~ +!!! error TS2345: Argument of type '"- 1"' is not assignable to parameter of type '`${number}`'. + numbers("-/**/1"); + ~~~~~~~~ +!!! error TS2345: Argument of type '"-/**/1"' is not assignable to parameter of type '`${number}`'. + + declare function bigints(x: `${bigint}`): void; + // the following should work + bigints("1"); + bigints("-1"); + bigints("0"); + bigints("0b1"); + bigints("0x1"); + bigints("0o1"); + + // bigints do not allow scientific notation in their parsing/scanning, so these are all errors + bigints("1e21"); + ~~~~~~ +!!! error TS2345: Argument of type '"1e21"' is not assignable to parameter of type '`${bigint}`'. + bigints("1E21"); + ~~~~~~ +!!! error TS2345: Argument of type '"1E21"' is not assignable to parameter of type '`${bigint}`'. + bigints("1e-21"); + ~~~~~~~ +!!! error TS2345: Argument of type '"1e-21"' is not assignable to parameter of type '`${bigint}`'. + bigints("1E-21"); + ~~~~~~~ +!!! error TS2345: Argument of type '"1E-21"' is not assignable to parameter of type '`${bigint}`'. + + // these are all errors because they're not big_int_s + bigints("1.0"); + ~~~~~ +!!! error TS2345: Argument of type '"1.0"' is not assignable to parameter of type '`${bigint}`'. + bigints("1.1"); + ~~~~~ +!!! error TS2345: Argument of type '"1.1"' is not assignable to parameter of type '`${bigint}`'. + bigints("-1.1"); + ~~~~~~ +!!! error TS2345: Argument of type '"-1.1"' is not assignable to parameter of type '`${bigint}`'. + bigints("-1.1e-10"); + ~~~~~~~~~~ +!!! error TS2345: Argument of type '"-1.1e-10"' is not assignable to parameter of type '`${bigint}`'. + bigints("-1.1E-10"); + ~~~~~~~~~~ +!!! error TS2345: Argument of type '"-1.1E-10"' is not assignable to parameter of type '`${bigint}`'. + bigints("1.1e-10"); + ~~~~~~~~~ +!!! error TS2345: Argument of type '"1.1e-10"' is not assignable to parameter of type '`${bigint}`'. + + // the following should be errors since they're not numbers + bigints("?"); + ~~~ +!!! error TS2345: Argument of type '"?"' is not assignable to parameter of type '`${bigint}`'. + bigints("NaN"); + ~~~~~ +!!! error TS2345: Argument of type '"NaN"' is not assignable to parameter of type '`${bigint}`'. + bigints("Infinity"); + ~~~~~~~~~~ +!!! error TS2345: Argument of type '"Infinity"' is not assignable to parameter of type '`${bigint}`'. + bigints("+Infinity"); + ~~~~~~~~~~~ +!!! error TS2345: Argument of type '"+Infinity"' is not assignable to parameter of type '`${bigint}`'. + bigints("-Infinity"); + ~~~~~~~~~~~ +!!! error TS2345: Argument of type '"-Infinity"' is not assignable to parameter of type '`${bigint}`'. + bigints("1_000"); + ~~~~~~~ +!!! error TS2345: Argument of type '"1_000"' is not assignable to parameter of type '`${bigint}`'. + + // whitespace and comments aren't part of numbers + bigints("- 1"); + ~~~~~ +!!! error TS2345: Argument of type '"- 1"' is not assignable to parameter of type '`${bigint}`'. + bigints("-/**/1"); + ~~~~~~~~ +!!! error TS2345: Argument of type '"-/**/1"' is not assignable to parameter of type '`${bigint}`'. + + // the following should be errors since they don't match the pattern + bigints("a10n"); + ~~~~~~ +!!! error TS2345: Argument of type '"a10n"' is not assignable to parameter of type '`${bigint}`'. + bigints("10an"); + ~~~~~~ +!!! error TS2345: Argument of type '"10an"' is not assignable to parameter of type '`${bigint}`'. + + // the following should all be errors because the `BigInt` constructor (and thus bigint parsing) doesn't take the trailing `n` used in literals + bigints("1n"); + ~~~~ +!!! error TS2345: Argument of type '"1n"' is not assignable to parameter of type '`${bigint}`'. + bigints("-1n"); + ~~~~~ +!!! error TS2345: Argument of type '"-1n"' is not assignable to parameter of type '`${bigint}`'. + bigints("0n"); + ~~~~ +!!! error TS2345: Argument of type '"0n"' is not assignable to parameter of type '`${bigint}`'. + bigints("0b1n"); + ~~~~~~ +!!! error TS2345: Argument of type '"0b1n"' is not assignable to parameter of type '`${bigint}`'. + bigints("0x1n"); + ~~~~~~ +!!! error TS2345: Argument of type '"0x1n"' is not assignable to parameter of type '`${bigint}`'. + bigints("0o1n"); + ~~~~~~ +!!! error TS2345: Argument of type '"0o1n"' is not assignable to parameter of type '`${bigint}`'. + bigints("1e21n"); + ~~~~~~~ +!!! error TS2345: Argument of type '"1e21n"' is not assignable to parameter of type '`${bigint}`'. + bigints("1E21n"); + ~~~~~~~ +!!! error TS2345: Argument of type '"1E21n"' is not assignable to parameter of type '`${bigint}`'. + bigints("1e-21n"); + ~~~~~~~~ +!!! error TS2345: Argument of type '"1e-21n"' is not assignable to parameter of type '`${bigint}`'. + bigints("1E-21n"); + ~~~~~~~~ +!!! error TS2345: Argument of type '"1E-21n"' is not assignable to parameter of type '`${bigint}`'. + bigints("1.1n"); + ~~~~~~ +!!! error TS2345: Argument of type '"1.1n"' is not assignable to parameter of type '`${bigint}`'. + bigints("-1.1n"); + ~~~~~~~ +!!! error TS2345: Argument of type '"-1.1n"' is not assignable to parameter of type '`${bigint}`'. + bigints("-1.1e-10n"); + ~~~~~~~~~~~ +!!! error TS2345: Argument of type '"-1.1e-10n"' is not assignable to parameter of type '`${bigint}`'. + bigints("-1.1E-10n"); + ~~~~~~~~~~~ +!!! error TS2345: Argument of type '"-1.1E-10n"' is not assignable to parameter of type '`${bigint}`'. + bigints("1.1e-10n"); + ~~~~~~~~~~ +!!! error TS2345: Argument of type '"1.1e-10n"' is not assignable to parameter of type '`${bigint}`'. + + type AStr = `a${string}`; + type ANum = `a${number}`; + type AAny = `a${any}`; + + declare var str: AStr; + declare var num: ANum; + declare var anyish: AAny; + + // not ok + num = str; + ~~~ +!!! error TS2322: Type '`a${string}`' is not assignable to type '`a${number}`'. + anyish = `bno` + ~~~~~~ +!!! error TS2322: Type '"bno"' is not assignable to type '`a${any}`'. + + // ok + str = num; + anyish = str; + str = anyish; + anyish = num; + num = anyish; + anyish = `aok` + + + // Validates variance isn't measured as strictly covariant + type AGen = {field: `a${T}`}; + const shouldWork1: AGen = null as any as AGen<"yes">; + const shouldWork2: AGen = null as any as AGen; + + // validates concatenation of patterns + type A = `${number}`; + type B = `${A} ${A}`; + const exampleBad: B = "anything"; // fails + ~~~~~~~~~~ +!!! error TS2322: Type '"anything"' is not assignable to type '`${number} ${number}`'. + const exampleGood: B = "1 2"; // ok \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralTypesPatterns.js b/tests/baselines/reference/templateLiteralTypesPatterns.js new file mode 100644 index 0000000000000..51fdba1113c1c --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypesPatterns.js @@ -0,0 +1,282 @@ +//// [templateLiteralTypesPatterns.ts] +type RequiresLeadingSlash = `/${string}`; + +// ok +const a: RequiresLeadingSlash = "/bin"; + +// not ok +const b: RequiresLeadingSlash = "no slash"; + +type Protocol = `${T}://${U}`; +function download(hostSpec: Protocol<"http" | "https" | "ftp", string>) { } +// ok, has protocol +download("http://example.com/protocol"); +// issues error - no protocol +download("example.com/noprotocol"); +// issues error, incorrect protocol +download("gopher://example.com/protocol"); + +const q: RequiresLeadingSlash extends string ? true : false = true; + +declare function bools(x: `${boolean}`): void; +// ok +bools("true"); +bools("false"); + +// not ok +bools("other"); + +type Pat = `${T}` +declare function nullishes(x: Pat): void; +// ok +nullishes("null"); +nullishes("undefined"); + +// not ok +nullishes("0"); +nullishes("false"); +nullishes("NaN"); +nullishes(""); +nullishes("other"); + +declare function numbers(x: `${number}`): void; +// the following should work +numbers("1"); +numbers("-1"); +numbers("0"); +numbers("0b1"); +numbers("0x1"); +numbers("0o1"); +numbers("1e21"); +numbers("1E21"); +numbers("1e-21"); +numbers("1E-21"); +numbers("1.1"); +numbers("-1.1"); +numbers("-1.1e-10"); +numbers("-1.1E-10"); +numbers("1.1e-10"); + +// the following should be errors since they're not numbers +numbers("?"); +numbers("NaN"); +numbers("Infinity"); +numbers("+Infinity"); +numbers("-Infinity"); +numbers("1_000"); + +// the following should be errors since they don't match the pattern +numbers("a10"); +numbers("10a"); + +// whitespace and comments aren't part of numbers +numbers("- 1"); +numbers("-/**/1"); + +declare function bigints(x: `${bigint}`): void; +// the following should work +bigints("1"); +bigints("-1"); +bigints("0"); +bigints("0b1"); +bigints("0x1"); +bigints("0o1"); + +// bigints do not allow scientific notation in their parsing/scanning, so these are all errors +bigints("1e21"); +bigints("1E21"); +bigints("1e-21"); +bigints("1E-21"); + +// these are all errors because they're not big_int_s +bigints("1.0"); +bigints("1.1"); +bigints("-1.1"); +bigints("-1.1e-10"); +bigints("-1.1E-10"); +bigints("1.1e-10"); + +// the following should be errors since they're not numbers +bigints("?"); +bigints("NaN"); +bigints("Infinity"); +bigints("+Infinity"); +bigints("-Infinity"); +bigints("1_000"); + +// whitespace and comments aren't part of numbers +bigints("- 1"); +bigints("-/**/1"); + +// the following should be errors since they don't match the pattern +bigints("a10n"); +bigints("10an"); + +// the following should all be errors because the `BigInt` constructor (and thus bigint parsing) doesn't take the trailing `n` used in literals +bigints("1n"); +bigints("-1n"); +bigints("0n"); +bigints("0b1n"); +bigints("0x1n"); +bigints("0o1n"); +bigints("1e21n"); +bigints("1E21n"); +bigints("1e-21n"); +bigints("1E-21n"); +bigints("1.1n"); +bigints("-1.1n"); +bigints("-1.1e-10n"); +bigints("-1.1E-10n"); +bigints("1.1e-10n"); + +type AStr = `a${string}`; +type ANum = `a${number}`; +type AAny = `a${any}`; + +declare var str: AStr; +declare var num: ANum; +declare var anyish: AAny; + +// not ok +num = str; +anyish = `bno` + +// ok +str = num; +anyish = str; +str = anyish; +anyish = num; +num = anyish; +anyish = `aok` + + +// Validates variance isn't measured as strictly covariant +type AGen = {field: `a${T}`}; +const shouldWork1: AGen = null as any as AGen<"yes">; +const shouldWork2: AGen = null as any as AGen; + +// validates concatenation of patterns +type A = `${number}`; +type B = `${A} ${A}`; +const exampleBad: B = "anything"; // fails +const exampleGood: B = "1 2"; // ok + +//// [templateLiteralTypesPatterns.js] +"use strict"; +// ok +var a = "/bin"; +// not ok +var b = "no slash"; +function download(hostSpec) { } +// ok, has protocol +download("http://example.com/protocol"); +// issues error - no protocol +download("example.com/noprotocol"); +// issues error, incorrect protocol +download("gopher://example.com/protocol"); +var q = true; +// ok +bools("true"); +bools("false"); +// not ok +bools("other"); +// ok +nullishes("null"); +nullishes("undefined"); +// not ok +nullishes("0"); +nullishes("false"); +nullishes("NaN"); +nullishes(""); +nullishes("other"); +// the following should work +numbers("1"); +numbers("-1"); +numbers("0"); +numbers("0b1"); +numbers("0x1"); +numbers("0o1"); +numbers("1e21"); +numbers("1E21"); +numbers("1e-21"); +numbers("1E-21"); +numbers("1.1"); +numbers("-1.1"); +numbers("-1.1e-10"); +numbers("-1.1E-10"); +numbers("1.1e-10"); +// the following should be errors since they're not numbers +numbers("?"); +numbers("NaN"); +numbers("Infinity"); +numbers("+Infinity"); +numbers("-Infinity"); +numbers("1_000"); +// the following should be errors since they don't match the pattern +numbers("a10"); +numbers("10a"); +// whitespace and comments aren't part of numbers +numbers("- 1"); +numbers("-/**/1"); +// the following should work +bigints("1"); +bigints("-1"); +bigints("0"); +bigints("0b1"); +bigints("0x1"); +bigints("0o1"); +// bigints do not allow scientific notation in their parsing/scanning, so these are all errors +bigints("1e21"); +bigints("1E21"); +bigints("1e-21"); +bigints("1E-21"); +// these are all errors because they're not big_int_s +bigints("1.0"); +bigints("1.1"); +bigints("-1.1"); +bigints("-1.1e-10"); +bigints("-1.1E-10"); +bigints("1.1e-10"); +// the following should be errors since they're not numbers +bigints("?"); +bigints("NaN"); +bigints("Infinity"); +bigints("+Infinity"); +bigints("-Infinity"); +bigints("1_000"); +// whitespace and comments aren't part of numbers +bigints("- 1"); +bigints("-/**/1"); +// the following should be errors since they don't match the pattern +bigints("a10n"); +bigints("10an"); +// the following should all be errors because the `BigInt` constructor (and thus bigint parsing) doesn't take the trailing `n` used in literals +bigints("1n"); +bigints("-1n"); +bigints("0n"); +bigints("0b1n"); +bigints("0x1n"); +bigints("0o1n"); +bigints("1e21n"); +bigints("1E21n"); +bigints("1e-21n"); +bigints("1E-21n"); +bigints("1.1n"); +bigints("-1.1n"); +bigints("-1.1e-10n"); +bigints("-1.1E-10n"); +bigints("1.1e-10n"); +// not ok +num = str; +anyish = "bno"; +// ok +str = num; +anyish = str; +str = anyish; +anyish = num; +num = anyish; +anyish = "aok"; +var shouldWork1 = null; +var shouldWork2 = null; +var exampleBad = "anything"; // fails +var exampleGood = "1 2"; // ok diff --git a/tests/baselines/reference/templateLiteralTypesPatterns.symbols b/tests/baselines/reference/templateLiteralTypesPatterns.symbols new file mode 100644 index 0000000000000..51810c9959efa --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypesPatterns.symbols @@ -0,0 +1,395 @@ +=== tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts === +type RequiresLeadingSlash = `/${string}`; +>RequiresLeadingSlash : Symbol(RequiresLeadingSlash, Decl(templateLiteralTypesPatterns.ts, 0, 0)) + +// ok +const a: RequiresLeadingSlash = "/bin"; +>a : Symbol(a, Decl(templateLiteralTypesPatterns.ts, 3, 5)) +>RequiresLeadingSlash : Symbol(RequiresLeadingSlash, Decl(templateLiteralTypesPatterns.ts, 0, 0)) + +// not ok +const b: RequiresLeadingSlash = "no slash"; +>b : Symbol(b, Decl(templateLiteralTypesPatterns.ts, 6, 5)) +>RequiresLeadingSlash : Symbol(RequiresLeadingSlash, Decl(templateLiteralTypesPatterns.ts, 0, 0)) + +type Protocol = `${T}://${U}`; +>Protocol : Symbol(Protocol, Decl(templateLiteralTypesPatterns.ts, 6, 43)) +>T : Symbol(T, Decl(templateLiteralTypesPatterns.ts, 8, 14)) +>U : Symbol(U, Decl(templateLiteralTypesPatterns.ts, 8, 31)) +>T : Symbol(T, Decl(templateLiteralTypesPatterns.ts, 8, 14)) +>U : Symbol(U, Decl(templateLiteralTypesPatterns.ts, 8, 31)) + +function download(hostSpec: Protocol<"http" | "https" | "ftp", string>) { } +>download : Symbol(download, Decl(templateLiteralTypesPatterns.ts, 8, 66)) +>hostSpec : Symbol(hostSpec, Decl(templateLiteralTypesPatterns.ts, 9, 18)) +>Protocol : Symbol(Protocol, Decl(templateLiteralTypesPatterns.ts, 6, 43)) + +// ok, has protocol +download("http://example.com/protocol"); +>download : Symbol(download, Decl(templateLiteralTypesPatterns.ts, 8, 66)) + +// issues error - no protocol +download("example.com/noprotocol"); +>download : Symbol(download, Decl(templateLiteralTypesPatterns.ts, 8, 66)) + +// issues error, incorrect protocol +download("gopher://example.com/protocol"); +>download : Symbol(download, Decl(templateLiteralTypesPatterns.ts, 8, 66)) + +const q: RequiresLeadingSlash extends string ? true : false = true; +>q : Symbol(q, Decl(templateLiteralTypesPatterns.ts, 17, 5)) +>RequiresLeadingSlash : Symbol(RequiresLeadingSlash, Decl(templateLiteralTypesPatterns.ts, 0, 0)) + +declare function bools(x: `${boolean}`): void; +>bools : Symbol(bools, Decl(templateLiteralTypesPatterns.ts, 17, 67)) +>x : Symbol(x, Decl(templateLiteralTypesPatterns.ts, 19, 23)) + +// ok +bools("true"); +>bools : Symbol(bools, Decl(templateLiteralTypesPatterns.ts, 17, 67)) + +bools("false"); +>bools : Symbol(bools, Decl(templateLiteralTypesPatterns.ts, 17, 67)) + +// not ok +bools("other"); +>bools : Symbol(bools, Decl(templateLiteralTypesPatterns.ts, 17, 67)) + +type Pat = `${T}` +>Pat : Symbol(Pat, Decl(templateLiteralTypesPatterns.ts, 25, 15)) +>T : Symbol(T, Decl(templateLiteralTypesPatterns.ts, 27, 9)) +>T : Symbol(T, Decl(templateLiteralTypesPatterns.ts, 27, 9)) + +declare function nullishes(x: Pat): void; +>nullishes : Symbol(nullishes, Decl(templateLiteralTypesPatterns.ts, 27, 54)) +>x : Symbol(x, Decl(templateLiteralTypesPatterns.ts, 28, 27)) +>Pat : Symbol(Pat, Decl(templateLiteralTypesPatterns.ts, 25, 15)) + +// ok +nullishes("null"); +>nullishes : Symbol(nullishes, Decl(templateLiteralTypesPatterns.ts, 27, 54)) + +nullishes("undefined"); +>nullishes : Symbol(nullishes, Decl(templateLiteralTypesPatterns.ts, 27, 54)) + +// not ok +nullishes("0"); +>nullishes : Symbol(nullishes, Decl(templateLiteralTypesPatterns.ts, 27, 54)) + +nullishes("false"); +>nullishes : Symbol(nullishes, Decl(templateLiteralTypesPatterns.ts, 27, 54)) + +nullishes("NaN"); +>nullishes : Symbol(nullishes, Decl(templateLiteralTypesPatterns.ts, 27, 54)) + +nullishes(""); +>nullishes : Symbol(nullishes, Decl(templateLiteralTypesPatterns.ts, 27, 54)) + +nullishes("other"); +>nullishes : Symbol(nullishes, Decl(templateLiteralTypesPatterns.ts, 27, 54)) + +declare function numbers(x: `${number}`): void; +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) +>x : Symbol(x, Decl(templateLiteralTypesPatterns.ts, 40, 25)) + +// the following should work +numbers("1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("-1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("0"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("0b1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("0x1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("0o1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("1e21"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("1E21"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("1e-21"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("1E-21"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("1.1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("-1.1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("-1.1e-10"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("-1.1E-10"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("1.1e-10"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +// the following should be errors since they're not numbers +numbers("?"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("NaN"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("Infinity"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("+Infinity"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("-Infinity"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("1_000"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +// the following should be errors since they don't match the pattern +numbers("a10"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("10a"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +// whitespace and comments aren't part of numbers +numbers("- 1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +numbers("-/**/1"); +>numbers : Symbol(numbers, Decl(templateLiteralTypesPatterns.ts, 38, 19)) + +declare function bigints(x: `${bigint}`): void; +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) +>x : Symbol(x, Decl(templateLiteralTypesPatterns.ts, 74, 25)) + +// the following should work +bigints("1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("0"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("0b1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("0x1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("0o1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +// bigints do not allow scientific notation in their parsing/scanning, so these are all errors +bigints("1e21"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1E21"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1e-21"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1E-21"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +// these are all errors because they're not big_int_s +bigints("1.0"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1.1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-1.1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-1.1e-10"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-1.1E-10"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1.1e-10"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +// the following should be errors since they're not numbers +bigints("?"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("NaN"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("Infinity"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("+Infinity"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-Infinity"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1_000"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +// whitespace and comments aren't part of numbers +bigints("- 1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-/**/1"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +// the following should be errors since they don't match the pattern +bigints("a10n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("10an"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +// the following should all be errors because the `BigInt` constructor (and thus bigint parsing) doesn't take the trailing `n` used in literals +bigints("1n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-1n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("0n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("0b1n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("0x1n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("0o1n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1e21n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1E21n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1e-21n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1E-21n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1.1n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-1.1n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-1.1e-10n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("-1.1E-10n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +bigints("1.1e-10n"); +>bigints : Symbol(bigints, Decl(templateLiteralTypesPatterns.ts, 72, 18)) + +type AStr = `a${string}`; +>AStr : Symbol(AStr, Decl(templateLiteralTypesPatterns.ts, 128, 20)) + +type ANum = `a${number}`; +>ANum : Symbol(ANum, Decl(templateLiteralTypesPatterns.ts, 130, 25)) + +type AAny = `a${any}`; +>AAny : Symbol(AAny, Decl(templateLiteralTypesPatterns.ts, 131, 25)) + +declare var str: AStr; +>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11)) +>AStr : Symbol(AStr, Decl(templateLiteralTypesPatterns.ts, 128, 20)) + +declare var num: ANum; +>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11)) +>ANum : Symbol(ANum, Decl(templateLiteralTypesPatterns.ts, 130, 25)) + +declare var anyish: AAny; +>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11)) +>AAny : Symbol(AAny, Decl(templateLiteralTypesPatterns.ts, 131, 25)) + +// not ok +num = str; +>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11)) +>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11)) + +anyish = `bno` +>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11)) + +// ok +str = num; +>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11)) +>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11)) + +anyish = str; +>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11)) +>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11)) + +str = anyish; +>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 134, 11)) +>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11)) + +anyish = num; +>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11)) +>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11)) + +num = anyish; +>num : Symbol(num, Decl(templateLiteralTypesPatterns.ts, 135, 11)) +>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11)) + +anyish = `aok` +>anyish : Symbol(anyish, Decl(templateLiteralTypesPatterns.ts, 136, 11)) + + +// Validates variance isn't measured as strictly covariant +type AGen = {field: `a${T}`}; +>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14)) +>T : Symbol(T, Decl(templateLiteralTypesPatterns.ts, 152, 10)) +>field : Symbol(field, Decl(templateLiteralTypesPatterns.ts, 152, 40)) +>T : Symbol(T, Decl(templateLiteralTypesPatterns.ts, 152, 10)) + +const shouldWork1: AGen = null as any as AGen<"yes">; +>shouldWork1 : Symbol(shouldWork1, Decl(templateLiteralTypesPatterns.ts, 153, 5)) +>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14)) +>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14)) + +const shouldWork2: AGen = null as any as AGen; +>shouldWork2 : Symbol(shouldWork2, Decl(templateLiteralTypesPatterns.ts, 154, 5)) +>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14)) +>AGen : Symbol(AGen, Decl(templateLiteralTypesPatterns.ts, 148, 14)) + +// validates concatenation of patterns +type A = `${number}`; +>A : Symbol(A, Decl(templateLiteralTypesPatterns.ts, 154, 62)) + +type B = `${A} ${A}`; +>B : Symbol(B, Decl(templateLiteralTypesPatterns.ts, 157, 21)) +>A : Symbol(A, Decl(templateLiteralTypesPatterns.ts, 154, 62)) +>A : Symbol(A, Decl(templateLiteralTypesPatterns.ts, 154, 62)) + +const exampleBad: B = "anything"; // fails +>exampleBad : Symbol(exampleBad, Decl(templateLiteralTypesPatterns.ts, 159, 5)) +>B : Symbol(B, Decl(templateLiteralTypesPatterns.ts, 157, 21)) + +const exampleGood: B = "1 2"; // ok +>exampleGood : Symbol(exampleGood, Decl(templateLiteralTypesPatterns.ts, 160, 5)) +>B : Symbol(B, Decl(templateLiteralTypesPatterns.ts, 157, 21)) + diff --git a/tests/baselines/reference/templateLiteralTypesPatterns.types b/tests/baselines/reference/templateLiteralTypesPatterns.types new file mode 100644 index 0000000000000..02dc985ee5bfc --- /dev/null +++ b/tests/baselines/reference/templateLiteralTypesPatterns.types @@ -0,0 +1,554 @@ +=== tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts === +type RequiresLeadingSlash = `/${string}`; +>RequiresLeadingSlash : `/${string}` + +// ok +const a: RequiresLeadingSlash = "/bin"; +>a : `/${string}` +>"/bin" : "/bin" + +// not ok +const b: RequiresLeadingSlash = "no slash"; +>b : `/${string}` +>"no slash" : "no slash" + +type Protocol = `${T}://${U}`; +>Protocol : `${T}://${U}` + +function download(hostSpec: Protocol<"http" | "https" | "ftp", string>) { } +>download : (hostSpec: Protocol<"http" | "https" | "ftp", string>) => void +>hostSpec : `http://${string}` | `https://${string}` | `ftp://${string}` + +// ok, has protocol +download("http://example.com/protocol"); +>download("http://example.com/protocol") : void +>download : (hostSpec: `http://${string}` | `https://${string}` | `ftp://${string}`) => void +>"http://example.com/protocol" : "http://example.com/protocol" + +// issues error - no protocol +download("example.com/noprotocol"); +>download("example.com/noprotocol") : void +>download : (hostSpec: `http://${string}` | `https://${string}` | `ftp://${string}`) => void +>"example.com/noprotocol" : "example.com/noprotocol" + +// issues error, incorrect protocol +download("gopher://example.com/protocol"); +>download("gopher://example.com/protocol") : void +>download : (hostSpec: `http://${string}` | `https://${string}` | `ftp://${string}`) => void +>"gopher://example.com/protocol" : "gopher://example.com/protocol" + +const q: RequiresLeadingSlash extends string ? true : false = true; +>q : true +>true : true +>false : false +>true : true + +declare function bools(x: `${boolean}`): void; +>bools : (x: `${boolean}`) => void +>x : "false" | "true" + +// ok +bools("true"); +>bools("true") : void +>bools : (x: "false" | "true") => void +>"true" : "true" + +bools("false"); +>bools("false") : void +>bools : (x: "false" | "true") => void +>"false" : "false" + +// not ok +bools("other"); +>bools("other") : void +>bools : (x: "false" | "true") => void +>"other" : "other" + +type Pat = `${T}` +>Pat : `${T}` +>null : null + +declare function nullishes(x: Pat): void; +>nullishes : (x: Pat) => void +>x : "undefined" | "null" +>null : null + +// ok +nullishes("null"); +>nullishes("null") : void +>nullishes : (x: "undefined" | "null") => void +>"null" : "null" + +nullishes("undefined"); +>nullishes("undefined") : void +>nullishes : (x: "undefined" | "null") => void +>"undefined" : "undefined" + +// not ok +nullishes("0"); +>nullishes("0") : void +>nullishes : (x: "undefined" | "null") => void +>"0" : "0" + +nullishes("false"); +>nullishes("false") : void +>nullishes : (x: "undefined" | "null") => void +>"false" : "false" + +nullishes("NaN"); +>nullishes("NaN") : void +>nullishes : (x: "undefined" | "null") => void +>"NaN" : "NaN" + +nullishes(""); +>nullishes("") : void +>nullishes : (x: "undefined" | "null") => void +>"" : "" + +nullishes("other"); +>nullishes("other") : void +>nullishes : (x: "undefined" | "null") => void +>"other" : "other" + +declare function numbers(x: `${number}`): void; +>numbers : (x: `${number}`) => void +>x : `${number}` + +// the following should work +numbers("1"); +>numbers("1") : void +>numbers : (x: `${number}`) => void +>"1" : "1" + +numbers("-1"); +>numbers("-1") : void +>numbers : (x: `${number}`) => void +>"-1" : "-1" + +numbers("0"); +>numbers("0") : void +>numbers : (x: `${number}`) => void +>"0" : "0" + +numbers("0b1"); +>numbers("0b1") : void +>numbers : (x: `${number}`) => void +>"0b1" : "0b1" + +numbers("0x1"); +>numbers("0x1") : void +>numbers : (x: `${number}`) => void +>"0x1" : "0x1" + +numbers("0o1"); +>numbers("0o1") : void +>numbers : (x: `${number}`) => void +>"0o1" : "0o1" + +numbers("1e21"); +>numbers("1e21") : void +>numbers : (x: `${number}`) => void +>"1e21" : "1e21" + +numbers("1E21"); +>numbers("1E21") : void +>numbers : (x: `${number}`) => void +>"1E21" : "1E21" + +numbers("1e-21"); +>numbers("1e-21") : void +>numbers : (x: `${number}`) => void +>"1e-21" : "1e-21" + +numbers("1E-21"); +>numbers("1E-21") : void +>numbers : (x: `${number}`) => void +>"1E-21" : "1E-21" + +numbers("1.1"); +>numbers("1.1") : void +>numbers : (x: `${number}`) => void +>"1.1" : "1.1" + +numbers("-1.1"); +>numbers("-1.1") : void +>numbers : (x: `${number}`) => void +>"-1.1" : "-1.1" + +numbers("-1.1e-10"); +>numbers("-1.1e-10") : void +>numbers : (x: `${number}`) => void +>"-1.1e-10" : "-1.1e-10" + +numbers("-1.1E-10"); +>numbers("-1.1E-10") : void +>numbers : (x: `${number}`) => void +>"-1.1E-10" : "-1.1E-10" + +numbers("1.1e-10"); +>numbers("1.1e-10") : void +>numbers : (x: `${number}`) => void +>"1.1e-10" : "1.1e-10" + +// the following should be errors since they're not numbers +numbers("?"); +>numbers("?") : void +>numbers : (x: `${number}`) => void +>"?" : "?" + +numbers("NaN"); +>numbers("NaN") : void +>numbers : (x: `${number}`) => void +>"NaN" : "NaN" + +numbers("Infinity"); +>numbers("Infinity") : void +>numbers : (x: `${number}`) => void +>"Infinity" : "Infinity" + +numbers("+Infinity"); +>numbers("+Infinity") : void +>numbers : (x: `${number}`) => void +>"+Infinity" : "+Infinity" + +numbers("-Infinity"); +>numbers("-Infinity") : void +>numbers : (x: `${number}`) => void +>"-Infinity" : "-Infinity" + +numbers("1_000"); +>numbers("1_000") : void +>numbers : (x: `${number}`) => void +>"1_000" : "1_000" + +// the following should be errors since they don't match the pattern +numbers("a10"); +>numbers("a10") : void +>numbers : (x: `${number}`) => void +>"a10" : "a10" + +numbers("10a"); +>numbers("10a") : void +>numbers : (x: `${number}`) => void +>"10a" : "10a" + +// whitespace and comments aren't part of numbers +numbers("- 1"); +>numbers("- 1") : void +>numbers : (x: `${number}`) => void +>"- 1" : "- 1" + +numbers("-/**/1"); +>numbers("-/**/1") : void +>numbers : (x: `${number}`) => void +>"-/**/1" : "-/**/1" + +declare function bigints(x: `${bigint}`): void; +>bigints : (x: `${bigint}`) => void +>x : `${bigint}` + +// the following should work +bigints("1"); +>bigints("1") : void +>bigints : (x: `${bigint}`) => void +>"1" : "1" + +bigints("-1"); +>bigints("-1") : void +>bigints : (x: `${bigint}`) => void +>"-1" : "-1" + +bigints("0"); +>bigints("0") : void +>bigints : (x: `${bigint}`) => void +>"0" : "0" + +bigints("0b1"); +>bigints("0b1") : void +>bigints : (x: `${bigint}`) => void +>"0b1" : "0b1" + +bigints("0x1"); +>bigints("0x1") : void +>bigints : (x: `${bigint}`) => void +>"0x1" : "0x1" + +bigints("0o1"); +>bigints("0o1") : void +>bigints : (x: `${bigint}`) => void +>"0o1" : "0o1" + +// bigints do not allow scientific notation in their parsing/scanning, so these are all errors +bigints("1e21"); +>bigints("1e21") : void +>bigints : (x: `${bigint}`) => void +>"1e21" : "1e21" + +bigints("1E21"); +>bigints("1E21") : void +>bigints : (x: `${bigint}`) => void +>"1E21" : "1E21" + +bigints("1e-21"); +>bigints("1e-21") : void +>bigints : (x: `${bigint}`) => void +>"1e-21" : "1e-21" + +bigints("1E-21"); +>bigints("1E-21") : void +>bigints : (x: `${bigint}`) => void +>"1E-21" : "1E-21" + +// these are all errors because they're not big_int_s +bigints("1.0"); +>bigints("1.0") : void +>bigints : (x: `${bigint}`) => void +>"1.0" : "1.0" + +bigints("1.1"); +>bigints("1.1") : void +>bigints : (x: `${bigint}`) => void +>"1.1" : "1.1" + +bigints("-1.1"); +>bigints("-1.1") : void +>bigints : (x: `${bigint}`) => void +>"-1.1" : "-1.1" + +bigints("-1.1e-10"); +>bigints("-1.1e-10") : void +>bigints : (x: `${bigint}`) => void +>"-1.1e-10" : "-1.1e-10" + +bigints("-1.1E-10"); +>bigints("-1.1E-10") : void +>bigints : (x: `${bigint}`) => void +>"-1.1E-10" : "-1.1E-10" + +bigints("1.1e-10"); +>bigints("1.1e-10") : void +>bigints : (x: `${bigint}`) => void +>"1.1e-10" : "1.1e-10" + +// the following should be errors since they're not numbers +bigints("?"); +>bigints("?") : void +>bigints : (x: `${bigint}`) => void +>"?" : "?" + +bigints("NaN"); +>bigints("NaN") : void +>bigints : (x: `${bigint}`) => void +>"NaN" : "NaN" + +bigints("Infinity"); +>bigints("Infinity") : void +>bigints : (x: `${bigint}`) => void +>"Infinity" : "Infinity" + +bigints("+Infinity"); +>bigints("+Infinity") : void +>bigints : (x: `${bigint}`) => void +>"+Infinity" : "+Infinity" + +bigints("-Infinity"); +>bigints("-Infinity") : void +>bigints : (x: `${bigint}`) => void +>"-Infinity" : "-Infinity" + +bigints("1_000"); +>bigints("1_000") : void +>bigints : (x: `${bigint}`) => void +>"1_000" : "1_000" + +// whitespace and comments aren't part of numbers +bigints("- 1"); +>bigints("- 1") : void +>bigints : (x: `${bigint}`) => void +>"- 1" : "- 1" + +bigints("-/**/1"); +>bigints("-/**/1") : void +>bigints : (x: `${bigint}`) => void +>"-/**/1" : "-/**/1" + +// the following should be errors since they don't match the pattern +bigints("a10n"); +>bigints("a10n") : void +>bigints : (x: `${bigint}`) => void +>"a10n" : "a10n" + +bigints("10an"); +>bigints("10an") : void +>bigints : (x: `${bigint}`) => void +>"10an" : "10an" + +// the following should all be errors because the `BigInt` constructor (and thus bigint parsing) doesn't take the trailing `n` used in literals +bigints("1n"); +>bigints("1n") : void +>bigints : (x: `${bigint}`) => void +>"1n" : "1n" + +bigints("-1n"); +>bigints("-1n") : void +>bigints : (x: `${bigint}`) => void +>"-1n" : "-1n" + +bigints("0n"); +>bigints("0n") : void +>bigints : (x: `${bigint}`) => void +>"0n" : "0n" + +bigints("0b1n"); +>bigints("0b1n") : void +>bigints : (x: `${bigint}`) => void +>"0b1n" : "0b1n" + +bigints("0x1n"); +>bigints("0x1n") : void +>bigints : (x: `${bigint}`) => void +>"0x1n" : "0x1n" + +bigints("0o1n"); +>bigints("0o1n") : void +>bigints : (x: `${bigint}`) => void +>"0o1n" : "0o1n" + +bigints("1e21n"); +>bigints("1e21n") : void +>bigints : (x: `${bigint}`) => void +>"1e21n" : "1e21n" + +bigints("1E21n"); +>bigints("1E21n") : void +>bigints : (x: `${bigint}`) => void +>"1E21n" : "1E21n" + +bigints("1e-21n"); +>bigints("1e-21n") : void +>bigints : (x: `${bigint}`) => void +>"1e-21n" : "1e-21n" + +bigints("1E-21n"); +>bigints("1E-21n") : void +>bigints : (x: `${bigint}`) => void +>"1E-21n" : "1E-21n" + +bigints("1.1n"); +>bigints("1.1n") : void +>bigints : (x: `${bigint}`) => void +>"1.1n" : "1.1n" + +bigints("-1.1n"); +>bigints("-1.1n") : void +>bigints : (x: `${bigint}`) => void +>"-1.1n" : "-1.1n" + +bigints("-1.1e-10n"); +>bigints("-1.1e-10n") : void +>bigints : (x: `${bigint}`) => void +>"-1.1e-10n" : "-1.1e-10n" + +bigints("-1.1E-10n"); +>bigints("-1.1E-10n") : void +>bigints : (x: `${bigint}`) => void +>"-1.1E-10n" : "-1.1E-10n" + +bigints("1.1e-10n"); +>bigints("1.1e-10n") : void +>bigints : (x: `${bigint}`) => void +>"1.1e-10n" : "1.1e-10n" + +type AStr = `a${string}`; +>AStr : `a${string}` + +type ANum = `a${number}`; +>ANum : `a${number}` + +type AAny = `a${any}`; +>AAny : `a${any}` + +declare var str: AStr; +>str : `a${string}` + +declare var num: ANum; +>num : `a${number}` + +declare var anyish: AAny; +>anyish : `a${any}` + +// not ok +num = str; +>num = str : `a${string}` +>num : `a${number}` +>str : `a${string}` + +anyish = `bno` +>anyish = `bno` : "bno" +>anyish : `a${any}` +>`bno` : "bno" + +// ok +str = num; +>str = num : `a${number}` +>str : `a${string}` +>num : `a${number}` + +anyish = str; +>anyish = str : `a${string}` +>anyish : `a${any}` +>str : `a${string}` + +str = anyish; +>str = anyish : `a${any}` +>str : `a${string}` +>anyish : `a${any}` + +anyish = num; +>anyish = num : `a${number}` +>anyish : `a${any}` +>num : `a${number}` + +num = anyish; +>num = anyish : `a${any}` +>num : `a${number}` +>anyish : `a${any}` + +anyish = `aok` +>anyish = `aok` : "aok" +>anyish : `a${any}` +>`aok` : "aok" + + +// Validates variance isn't measured as strictly covariant +type AGen = {field: `a${T}`}; +>AGen : AGen +>field : `a${T}` + +const shouldWork1: AGen = null as any as AGen<"yes">; +>shouldWork1 : AGen +>null as any as AGen<"yes"> : AGen<"yes"> +>null as any : any +>null : null + +const shouldWork2: AGen = null as any as AGen; +>shouldWork2 : AGen +>null as any as AGen : AGen +>null as any : any +>null : null + +// validates concatenation of patterns +type A = `${number}`; +>A : `${number}` + +type B = `${A} ${A}`; +>B : `${number} ${number}` + +const exampleBad: B = "anything"; // fails +>exampleBad : `${number} ${number}` +>"anything" : "anything" + +const exampleGood: B = "1 2"; // ok +>exampleGood : `${number} ${number}` +>"1 2" : "1 2" + diff --git a/tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts b/tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts new file mode 100644 index 0000000000000..064fb5b06d631 --- /dev/null +++ b/tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts @@ -0,0 +1,162 @@ +// @strict: true +type RequiresLeadingSlash = `/${string}`; + +// ok +const a: RequiresLeadingSlash = "/bin"; + +// not ok +const b: RequiresLeadingSlash = "no slash"; + +type Protocol = `${T}://${U}`; +function download(hostSpec: Protocol<"http" | "https" | "ftp", string>) { } +// ok, has protocol +download("http://example.com/protocol"); +// issues error - no protocol +download("example.com/noprotocol"); +// issues error, incorrect protocol +download("gopher://example.com/protocol"); + +const q: RequiresLeadingSlash extends string ? true : false = true; + +declare function bools(x: `${boolean}`): void; +// ok +bools("true"); +bools("false"); + +// not ok +bools("other"); + +type Pat = `${T}` +declare function nullishes(x: Pat): void; +// ok +nullishes("null"); +nullishes("undefined"); + +// not ok +nullishes("0"); +nullishes("false"); +nullishes("NaN"); +nullishes(""); +nullishes("other"); + +declare function numbers(x: `${number}`): void; +// the following should work +numbers("1"); +numbers("-1"); +numbers("0"); +numbers("0b1"); +numbers("0x1"); +numbers("0o1"); +numbers("1e21"); +numbers("1E21"); +numbers("1e-21"); +numbers("1E-21"); +numbers("1.1"); +numbers("-1.1"); +numbers("-1.1e-10"); +numbers("-1.1E-10"); +numbers("1.1e-10"); + +// the following should be errors since they're not numbers +numbers("?"); +numbers("NaN"); +numbers("Infinity"); +numbers("+Infinity"); +numbers("-Infinity"); +numbers("1_000"); + +// the following should be errors since they don't match the pattern +numbers("a10"); +numbers("10a"); + +// whitespace and comments aren't part of numbers +numbers("- 1"); +numbers("-/**/1"); + +declare function bigints(x: `${bigint}`): void; +// the following should work +bigints("1"); +bigints("-1"); +bigints("0"); +bigints("0b1"); +bigints("0x1"); +bigints("0o1"); + +// bigints do not allow scientific notation in their parsing/scanning, so these are all errors +bigints("1e21"); +bigints("1E21"); +bigints("1e-21"); +bigints("1E-21"); + +// these are all errors because they're not big_int_s +bigints("1.0"); +bigints("1.1"); +bigints("-1.1"); +bigints("-1.1e-10"); +bigints("-1.1E-10"); +bigints("1.1e-10"); + +// the following should be errors since they're not numbers +bigints("?"); +bigints("NaN"); +bigints("Infinity"); +bigints("+Infinity"); +bigints("-Infinity"); +bigints("1_000"); + +// whitespace and comments aren't part of numbers +bigints("- 1"); +bigints("-/**/1"); + +// the following should be errors since they don't match the pattern +bigints("a10n"); +bigints("10an"); + +// the following should all be errors because the `BigInt` constructor (and thus bigint parsing) doesn't take the trailing `n` used in literals +bigints("1n"); +bigints("-1n"); +bigints("0n"); +bigints("0b1n"); +bigints("0x1n"); +bigints("0o1n"); +bigints("1e21n"); +bigints("1E21n"); +bigints("1e-21n"); +bigints("1E-21n"); +bigints("1.1n"); +bigints("-1.1n"); +bigints("-1.1e-10n"); +bigints("-1.1E-10n"); +bigints("1.1e-10n"); + +type AStr = `a${string}`; +type ANum = `a${number}`; +type AAny = `a${any}`; + +declare var str: AStr; +declare var num: ANum; +declare var anyish: AAny; + +// not ok +num = str; +anyish = `bno` + +// ok +str = num; +anyish = str; +str = anyish; +anyish = num; +num = anyish; +anyish = `aok` + + +// Validates variance isn't measured as strictly covariant +type AGen = {field: `a${T}`}; +const shouldWork1: AGen = null as any as AGen<"yes">; +const shouldWork2: AGen = null as any as AGen; + +// validates concatenation of patterns +type A = `${number}`; +type B = `${A} ${A}`; +const exampleBad: B = "anything"; // fails +const exampleGood: B = "1 2"; // ok \ No newline at end of file