@@ -771,7 +771,7 @@ namespace ts {
771
771
const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]);
772
772
const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType;
773
773
const numberOrBigIntType = getUnionType([numberType, bigintType]);
774
- const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType]) ;
774
+ const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType, nullType, undefinedType]) as UnionType ;
775
775
776
776
const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(<TypeParameter>t) : t);
777
777
const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t);
@@ -13207,6 +13207,30 @@ namespace ts {
13207
13207
return true;
13208
13208
}
13209
13209
13210
+ /**
13211
+ * 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`
13212
+ */
13213
+ function extractRedundantTemplateLiterals(types: Type[]): boolean {
13214
+ let i = types.length;
13215
+ const literals = filter(types, t => !!(t.flags & TypeFlags.StringLiteral));
13216
+ while (i > 0) {
13217
+ i--;
13218
+ const t = types[i];
13219
+ if (!(t.flags & TypeFlags.TemplateLiteral)) continue;
13220
+ for (const t2 of literals) {
13221
+ if (isTypeSubtypeOf(t2, t)) {
13222
+ // eg, ``get${T}` & "getX"` is just `"getX"`
13223
+ orderedRemoveItemAt(types, i);
13224
+ break;
13225
+ }
13226
+ else if (isPatternLiteralType(t)) {
13227
+ return true;
13228
+ }
13229
+ }
13230
+ }
13231
+ return false;
13232
+ }
13233
+
13210
13234
function extractIrreducible(types: Type[], flag: TypeFlags) {
13211
13235
if (every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)))) {
13212
13236
for (let i = 0; i < types.length; i++) {
@@ -13355,7 +13379,12 @@ namespace ts {
13355
13379
}
13356
13380
}
13357
13381
else {
13358
- result = createIntersectionType(typeSet, aliasSymbol, aliasTypeArguments);
13382
+ if (includes & TypeFlags.TemplateLiteral && includes & TypeFlags.StringLiteral && extractRedundantTemplateLiterals(typeSet)) {
13383
+ result = neverType;
13384
+ }
13385
+ else {
13386
+ result = createIntersectionType(typeSet, aliasSymbol, aliasTypeArguments);
13387
+ }
13359
13388
}
13360
13389
intersectionTypes.set(id, result);
13361
13390
}
@@ -13531,7 +13560,7 @@ namespace ts {
13531
13560
function addSpans(texts: readonly string[], types: readonly Type[]): boolean {
13532
13561
for (let i = 0; i < types.length; i++) {
13533
13562
const t = types[i];
13534
- if (t.flags & TypeFlags.Literal) {
13563
+ if (t.flags & ( TypeFlags.Literal | TypeFlags.Null | TypeFlags.Undefined) ) {
13535
13564
text += getTemplateStringForType(t) || "";
13536
13565
text += texts[i + 1];
13537
13566
}
@@ -13540,7 +13569,7 @@ namespace ts {
13540
13569
if (!addSpans((<TemplateLiteralType>t).texts, (<TemplateLiteralType>t).types)) return false;
13541
13570
text += texts[i + 1];
13542
13571
}
13543
- else if (isGenericIndexType(t)) {
13572
+ else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t) ) {
13544
13573
newTypes.push(t);
13545
13574
newTexts.push(text);
13546
13575
text = texts[i + 1];
@@ -13558,6 +13587,8 @@ namespace ts {
13558
13587
type.flags & TypeFlags.NumberLiteral ? "" + (<NumberLiteralType>type).value :
13559
13588
type.flags & TypeFlags.BigIntLiteral ? pseudoBigIntToString((<BigIntLiteralType>type).value) :
13560
13589
type.flags & TypeFlags.BooleanLiteral ? (<IntrinsicType>type).intrinsicName :
13590
+ type.flags & TypeFlags.Null ? "null" :
13591
+ type.flags & TypeFlags.Undefined ? "undefined" :
13561
13592
undefined;
13562
13593
}
13563
13594
@@ -13817,6 +13848,14 @@ namespace ts {
13817
13848
accessNode;
13818
13849
}
13819
13850
13851
+ function isPatternLiteralPlaceholderType(type: Type) {
13852
+ return templateConstraintType.types.indexOf(type) !== -1 || !!(type.flags & TypeFlags.Any);
13853
+ }
13854
+
13855
+ function isPatternLiteralType(type: Type) {
13856
+ return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType);
13857
+ }
13858
+
13820
13859
function isGenericObjectType(type: Type): boolean {
13821
13860
if (type.flags & TypeFlags.UnionOrIntersection) {
13822
13861
if (!((<UnionOrIntersectionType>type).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) {
@@ -13836,7 +13875,7 @@ namespace ts {
13836
13875
}
13837
13876
return !!((<UnionOrIntersectionType>type).objectFlags & ObjectFlags.IsGenericIndexType);
13838
13877
}
13839
- return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping));
13878
+ return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type) ;
13840
13879
}
13841
13880
13842
13881
function isThisTypeParameter(type: Type): boolean {
@@ -14562,6 +14601,8 @@ namespace ts {
14562
14601
return !!(type.flags & TypeFlags.Literal) && (<LiteralType>type).freshType === type;
14563
14602
}
14564
14603
14604
+ function getLiteralType(value: string): StringLiteralType;
14605
+ function getLiteralType(value: string | number | PseudoBigInt, enumId?: number, symbol?: Symbol): LiteralType;
14565
14606
function getLiteralType(value: string | number | PseudoBigInt, enumId?: number, symbol?: Symbol) {
14566
14607
// We store all literal types in a single map with keys of the form '#NNN' and '@SSS',
14567
14608
// where NNN is the text representation of a numeric literal and SSS are the characters
@@ -17346,6 +17387,15 @@ namespace ts {
17346
17387
}
17347
17388
}
17348
17389
}
17390
+ else if (target.flags & TypeFlags.TemplateLiteral && source.flags & TypeFlags.StringLiteral) {
17391
+ if (isPatternLiteralType(target)) {
17392
+ // match all non-`string` segments
17393
+ const result = inferLiteralsFromTemplateLiteralType(source as StringLiteralType, target as TemplateLiteralType);
17394
+ if (result && every(result, (r, i) => isStringLiteralTypeValueParsableAsType(r, (target as TemplateLiteralType).types[i]))) {
17395
+ return Ternary.True;
17396
+ }
17397
+ }
17398
+ }
17349
17399
17350
17400
if (source.flags & TypeFlags.TypeVariable) {
17351
17401
if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
@@ -17386,8 +17436,15 @@ namespace ts {
17386
17436
}
17387
17437
}
17388
17438
else if (source.flags & TypeFlags.TemplateLiteral) {
17439
+ if (target.flags & TypeFlags.TemplateLiteral &&
17440
+ (source as TemplateLiteralType).texts.length === (target as TemplateLiteralType).texts.length &&
17441
+ (source as TemplateLiteralType).types.length === (target as TemplateLiteralType).types.length &&
17442
+ every((source as TemplateLiteralType).texts, (t, i) => t === (target as TemplateLiteralType).texts[i]) &&
17443
+ 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))) {
17444
+ return Ternary.True;
17445
+ }
17389
17446
const constraint = getBaseConstraintOfType(source);
17390
- if (constraint && (result = isRelatedTo(constraint, target, reportErrors))) {
17447
+ if (constraint && constraint !== source && (result = isRelatedTo(constraint, target, reportErrors))) {
17391
17448
resetErrorInfo(saveErrorInfo);
17392
17449
return result;
17393
17450
}
@@ -18308,12 +18365,12 @@ namespace ts {
18308
18365
18309
18366
if (type.flags & TypeFlags.Instantiable) {
18310
18367
const constraint = getConstraintOfType(type);
18311
- if (constraint) {
18368
+ if (constraint && constraint !== type ) {
18312
18369
return typeCouldHaveTopLevelSingletonTypes(constraint);
18313
18370
}
18314
18371
}
18315
18372
18316
- return isUnitType(type);
18373
+ return isUnitType(type) || !!(type.flags & TypeFlags.TemplateLiteral) ;
18317
18374
}
18318
18375
18319
18376
function getBestMatchingType(source: Type, target: UnionOrIntersectionType, isRelatedTo = compareTypesAssignable) {
@@ -19693,6 +19750,63 @@ namespace ts {
19693
19750
return !!(type.symbol && some(type.symbol.declarations, hasSkipDirectInferenceFlag));
19694
19751
}
19695
19752
19753
+ function isValidBigIntString(s: string): boolean {
19754
+ const scanner = createScanner(ScriptTarget.ESNext, /*skipTrivia*/ false);
19755
+ let success = true;
19756
+ scanner.setOnError(() => success = false);
19757
+ scanner.setText(s + "n");
19758
+ let result = scanner.scan();
19759
+ if (result === SyntaxKind.MinusToken) {
19760
+ result = scanner.scan();
19761
+ }
19762
+ const flags = scanner.getTokenFlags();
19763
+ // validate that
19764
+ // * scanning proceeded without error
19765
+ // * a bigint can be scanned, and that when it is scanned, it is
19766
+ // * the full length of the input string (so the scanner is one character beyond the augmented input length)
19767
+ // * it does not contain a numeric seperator (the `BigInt` constructor does not accept a numeric seperator in its input)
19768
+ return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator);
19769
+ }
19770
+
19771
+ function isStringLiteralTypeValueParsableAsType(s: StringLiteralType, target: Type): boolean {
19772
+ if (target.flags & TypeFlags.Union) {
19773
+ return !!forEachType(target, t => isStringLiteralTypeValueParsableAsType(s, t));
19774
+ }
19775
+ switch (target) {
19776
+ case stringType: return true;
19777
+ case numberType: return s.value !== "" && isFinite(+(s.value));
19778
+ case bigintType: return s.value !== "" && isValidBigIntString(s.value);
19779
+ // the next 4 should be handled in `getTemplateLiteralType`, as they are all exactly one value, but are here for completeness, just in case
19780
+ // this function is ever used on types which don't come from template literal holes
19781
+ case trueType: return s.value === "true";
19782
+ case falseType: return s.value === "false";
19783
+ case undefinedType: return s.value === "undefined";
19784
+ case nullType: return s.value === "null";
19785
+ default: return !!(target.flags & TypeFlags.Any);
19786
+ }
19787
+ }
19788
+
19789
+ function inferLiteralsFromTemplateLiteralType(source: StringLiteralType, target: TemplateLiteralType): StringLiteralType[] | undefined {
19790
+ const value = source.value;
19791
+ const texts = target.texts;
19792
+ const lastIndex = texts.length - 1;
19793
+ const startText = texts[0];
19794
+ const endText = texts[lastIndex];
19795
+ if (!(value.startsWith(startText) && value.endsWith(endText))) return undefined;
19796
+ const matches = [];
19797
+ const str = value.slice(startText.length, value.length - endText.length);
19798
+ let pos = 0;
19799
+ for (let i = 1; i < lastIndex; i++) {
19800
+ const delim = texts[i];
19801
+ const delimPos = delim.length > 0 ? str.indexOf(delim, pos) : pos < str.length ? pos + 1 : -1;
19802
+ if (delimPos < 0) return undefined;
19803
+ matches.push(getLiteralType(str.slice(pos, delimPos)));
19804
+ pos = delimPos + delim.length;
19805
+ }
19806
+ matches.push(getLiteralType(str.slice(pos)));
19807
+ return matches;
19808
+ }
19809
+
19696
19810
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
19697
19811
let bivariant = false;
19698
19812
let propagationType: Type;
@@ -20170,27 +20284,6 @@ namespace ts {
20170
20284
}
20171
20285
}
20172
20286
20173
- function inferLiteralsFromTemplateLiteralType(source: StringLiteralType, target: TemplateLiteralType): Type[] | undefined {
20174
- const value = source.value;
20175
- const texts = target.texts;
20176
- const lastIndex = texts.length - 1;
20177
- const startText = texts[0];
20178
- const endText = texts[lastIndex];
20179
- if (!(value.startsWith(startText) && value.endsWith(endText))) return undefined;
20180
- const matches = [];
20181
- const str = value.slice(startText.length, value.length - endText.length);
20182
- let pos = 0;
20183
- for (let i = 1; i < lastIndex; i++) {
20184
- const delim = texts[i];
20185
- const delimPos = delim.length > 0 ? str.indexOf(delim, pos) : pos < str.length ? pos + 1 : -1;
20186
- if (delimPos < 0) return undefined;
20187
- matches.push(getLiteralType(str.slice(pos, delimPos)));
20188
- pos = delimPos + delim.length;
20189
- }
20190
- matches.push(getLiteralType(str.slice(pos)));
20191
- return matches;
20192
- }
20193
-
20194
20287
function inferFromObjectTypes(source: Type, target: Type) {
20195
20288
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
20196
20289
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target))) {
@@ -31688,9 +31781,6 @@ namespace ts {
31688
31781
checkSourceElement(span.type);
31689
31782
const type = getTypeFromTypeNode(span.type);
31690
31783
checkTypeAssignableTo(type, templateConstraintType, span.type);
31691
- if (!everyType(type, t => !!(t.flags & TypeFlags.Literal) || isGenericIndexType(t))) {
31692
- error(span.type, Diagnostics.Template_literal_type_argument_0_is_not_literal_type_or_a_generic_type, typeToString(type));
31693
- }
31694
31784
}
31695
31785
getTypeFromTypeNode(node);
31696
31786
}
0 commit comments