@@ -14295,9 +14295,7 @@ namespace ts {
1429514295 return type.flags & TypeFlags.StringLiteral ? (<StringLiteralType>type).value :
1429614296 type.flags & TypeFlags.NumberLiteral ? "" + (<NumberLiteralType>type).value :
1429714297 type.flags & TypeFlags.BigIntLiteral ? pseudoBigIntToString((<BigIntLiteralType>type).value) :
14298- type.flags & TypeFlags.BooleanLiteral ? (<IntrinsicType>type).intrinsicName :
14299- type.flags & TypeFlags.Null ? "null" :
14300- type.flags & TypeFlags.Undefined ? "undefined" :
14298+ type.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) ? (<IntrinsicType>type).intrinsicName :
1430114299 undefined;
1430214300 }
1430314301
@@ -14577,7 +14575,7 @@ namespace ts {
1457714575 }
1457814576
1457914577 function isPatternLiteralPlaceholderType(type: Type) {
14580- return templateConstraintType.types.indexOf (type) !== -1 || !!(type.flags & TypeFlags.Any );
14578+ return !! (type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt) );
1458114579 }
1458214580
1458314581 function isPatternLiteralType(type: Type) {
@@ -18275,13 +18273,10 @@ namespace ts {
1827518273 return localResult;
1827618274 }
1827718275 }
18278- else if (target.flags & TypeFlags.TemplateLiteral && source.flags & TypeFlags.StringLiteral) {
18279- if (isPatternLiteralType(target)) {
18280- // match all non-`string` segments
18281- const result = inferLiteralsFromTemplateLiteralType(source as StringLiteralType, target as TemplateLiteralType);
18282- if (result && every(result, (r, i) => isStringLiteralTypeValueParsableAsType(r, (target as TemplateLiteralType).types[i]))) {
18283- return Ternary.True;
18284- }
18276+ else if (target.flags & TypeFlags.TemplateLiteral) {
18277+ const result = inferTypesFromTemplateLiteralType(source, target as TemplateLiteralType);
18278+ if (result && every(result, (r, i) => isValidTypeForTemplateLiteralPlaceholder(r, (target as TemplateLiteralType).types[i]))) {
18279+ return Ternary.True;
1828518280 }
1828618281 }
1828718282
@@ -20688,43 +20683,108 @@ namespace ts {
2068820683 return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator);
2068920684 }
2069020685
20691- function isStringLiteralTypeValueParsableAsType(s: StringLiteralType, target: Type): boolean {
20692- if (target.flags & TypeFlags.Union) {
20693- return someType(target, t => isStringLiteralTypeValueParsableAsType(s, t));
20694- }
20695- switch (target) {
20696- case stringType: return true;
20697- case numberType: return s.value !== "" && isFinite(+(s.value));
20698- case bigintType: return s.value !== "" && isValidBigIntString(s.value);
20699- // the next 4 should be handled in `getTemplateLiteralType`, as they are all exactly one value, but are here for completeness, just in case
20700- // this function is ever used on types which don't come from template literal holes
20701- case trueType: return s.value === "true";
20702- case falseType: return s.value === "false";
20703- case undefinedType: return s.value === "undefined";
20704- case nullType: return s.value === "null";
20705- default: return !!(target.flags & TypeFlags.Any);
20706- }
20707- }
20708-
20709- function inferLiteralsFromTemplateLiteralType(source: StringLiteralType, target: TemplateLiteralType): StringLiteralType[] | undefined {
20710- const value = source.value;
20711- const texts = target.texts;
20712- const lastIndex = texts.length - 1;
20713- const startText = texts[0];
20714- const endText = texts[lastIndex];
20715- if (!(value.startsWith(startText) && value.slice(startText.length).endsWith(endText))) return undefined;
20716- const matches = [];
20717- const str = value.slice(startText.length, value.length - endText.length);
20718- let pos = 0;
20719- for (let i = 1; i < lastIndex; i++) {
20720- const delim = texts[i];
20721- const delimPos = delim.length > 0 ? str.indexOf(delim, pos) : pos < str.length ? pos + 1 : -1;
20722- if (delimPos < 0) return undefined;
20723- matches.push(getLiteralType(str.slice(pos, delimPos)));
20724- pos = delimPos + delim.length;
20725- }
20726- matches.push(getLiteralType(str.slice(pos)));
20686+ function isValidTypeForTemplateLiteralPlaceholder(source: Type, target: Type): boolean {
20687+ if (source === target || target.flags & (TypeFlags.Any | TypeFlags.String)) {
20688+ return true;
20689+ }
20690+ if (source.flags & TypeFlags.StringLiteral) {
20691+ const value = (<StringLiteralType>source).value;
20692+ return !!(target.flags & TypeFlags.Number && value !== "" && isFinite(+value) ||
20693+ target.flags & TypeFlags.BigInt && value !== "" && isValidBigIntString(value) ||
20694+ target.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) && value === (<IntrinsicType>target).intrinsicName);
20695+ }
20696+ if (source.flags & TypeFlags.TemplateLiteral) {
20697+ const texts = (<TemplateLiteralType>source).texts;
20698+ return texts.length === 2 && texts[0] === "" && texts[1] === "" && isTypeAssignableTo((<TemplateLiteralType>source).types[0], target);
20699+ }
20700+ return isTypeAssignableTo(source, target);
20701+ }
20702+
20703+ function inferTypesFromTemplateLiteralType(source: Type, target: TemplateLiteralType): Type[] | undefined {
20704+ return source.flags & TypeFlags.StringLiteral ? inferFromLiteralPartsToTemplateLiteral([(<StringLiteralType>source).value], emptyArray, target) :
20705+ source.flags & TypeFlags.TemplateLiteral ?
20706+ arraysEqual((<TemplateLiteralType>source).texts, target.texts) ? map((<TemplateLiteralType>source).types, getStringLikeTypeForType) :
20707+ inferFromLiteralPartsToTemplateLiteral((<TemplateLiteralType>source).texts, (<TemplateLiteralType>source).types, target) :
20708+ undefined;
20709+ }
20710+
20711+ function getStringLikeTypeForType(type: Type) {
20712+ return type.flags & (TypeFlags.Any | TypeFlags.StringLike) ? type : getTemplateLiteralType(["", ""], [type]);
20713+ }
20714+
20715+ // This function infers from the text parts and type parts of a source literal to a target template literal. The number
20716+ // of text parts is always one more than the number of type parts, and a source string literal is treated as a source
20717+ // with one text part and zero type parts. The function returns an array of inferred string or template literal types
20718+ // corresponding to the placeholders in the target template literal, or undefined if the source doesn't match the target.
20719+ //
20720+ // We first check that the starting source text part matches the starting target text part, and that the ending source
20721+ // text part ends matches the ending target text part. We then iterate through the remaining target text parts, finding
20722+ // a match for each in the source and inferring string or template literal types created from the segments of the source
20723+ // that occur between the matches. During this iteration, seg holds the index of the current text part in the sourceTexts
20724+ // array and pos holds the current character position in the current text part.
20725+ //
20726+ // Consider inference from type `<<${string}>.<${number}-${number}>>` to type `<${string}.${string}>`, i.e.
20727+ // sourceTexts = ['<<', '>.<', '-', '>>']
20728+ // sourceTypes = [string, number, number]
20729+ // target.texts = ['<', '.', '>']
20730+ // We first match '<' in the target to the start of '<<' in the source and '>' in the target to the end of '>>' in
20731+ // the source. The first match for the '.' in target occurs at character 1 in the source text part at index 1, and thus
20732+ // the first inference is the template literal type `<${string}>`. The remainder of the source makes up the second
20733+ // inference, the template literal type `<${number}-${number}>`.
20734+ function inferFromLiteralPartsToTemplateLiteral(sourceTexts: readonly string[], sourceTypes: readonly Type[], target: TemplateLiteralType): Type[] | undefined {
20735+ const lastSourceIndex = sourceTexts.length - 1;
20736+ const sourceStartText = sourceTexts[0];
20737+ const sourceEndText = sourceTexts[lastSourceIndex];
20738+ const targetTexts = target.texts;
20739+ const lastTargetIndex = targetTexts.length - 1;
20740+ const targetStartText = targetTexts[0];
20741+ const targetEndText = targetTexts[lastTargetIndex];
20742+ if (lastSourceIndex === 0 && sourceStartText.length < targetStartText.length + targetEndText.length ||
20743+ !sourceStartText.startsWith(targetStartText) || !sourceEndText.endsWith(targetEndText)) return undefined;
20744+ const remainingEndText = sourceEndText.slice(0, sourceEndText.length - targetEndText.length);
20745+ const matches: Type[] = [];
20746+ let seg = 0;
20747+ let pos = targetStartText.length;
20748+ for (let i = 1; i < lastTargetIndex; i++) {
20749+ const delim = targetTexts[i];
20750+ if (delim.length > 0) {
20751+ let s = seg;
20752+ let p = pos;
20753+ while (true) {
20754+ p = getSourceText(s).indexOf(delim, p);
20755+ if (p >= 0) break;
20756+ s++;
20757+ if (s === sourceTexts.length) return undefined;
20758+ p = 0;
20759+ }
20760+ addMatch(s, p);
20761+ pos += delim.length;
20762+ }
20763+ else if (pos < getSourceText(seg).length) {
20764+ addMatch(seg, pos + 1);
20765+ }
20766+ else if (seg < lastSourceIndex) {
20767+ addMatch(seg + 1, 0);
20768+ }
20769+ else {
20770+ return undefined;
20771+ }
20772+ }
20773+ addMatch(lastSourceIndex, getSourceText(lastSourceIndex).length);
2072720774 return matches;
20775+ function getSourceText(index: number) {
20776+ return index < lastSourceIndex ? sourceTexts[index] : remainingEndText;
20777+ }
20778+ function addMatch(s: number, p: number) {
20779+ const matchType = s === seg ?
20780+ getLiteralType(getSourceText(s).slice(pos, p)) :
20781+ getTemplateLiteralType(
20782+ [sourceTexts[seg].slice(pos), ...sourceTexts.slice(seg + 1, s), getSourceText(s).slice(0, p)],
20783+ sourceTypes.slice(seg, s));
20784+ matches.push(matchType);
20785+ seg = s;
20786+ pos = p;
20787+ }
2072820788 }
2072920789
2073020790 function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
@@ -21189,9 +21249,7 @@ namespace ts {
2118921249 }
2119021250
2119121251 function inferToTemplateLiteralType(source: Type, target: TemplateLiteralType) {
21192- const matches = source.flags & TypeFlags.StringLiteral ? inferLiteralsFromTemplateLiteralType(<StringLiteralType>source, target) :
21193- source.flags & TypeFlags.TemplateLiteral && arraysEqual((<TemplateLiteralType>source).texts, target.texts) ? (<TemplateLiteralType>source).types :
21194- undefined;
21252+ const matches = inferTypesFromTemplateLiteralType(source, target);
2119521253 const types = target.types;
2119621254 for (let i = 0; i < types.length; i++) {
2119721255 inferFromTypes(matches ? matches[i] : neverType, types[i]);
0 commit comments