mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Allow intersections to be used as valid types for template literal placeholders (#54188)
This commit is contained in:
parent
cd391b066b
commit
28cd1fbd13
@ -16457,7 +16457,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) {
|
||||
const templates = filter(types, t => !!(t.flags & TypeFlags.TemplateLiteral) && isPatternLiteralType(t)) as TemplateLiteralType[];
|
||||
const templates = filter(types, t =>
|
||||
!!(t.flags & TypeFlags.TemplateLiteral) &&
|
||||
isPatternLiteralType(t) &&
|
||||
(t as TemplateLiteralType).types.every(t => !(t.flags & TypeFlags.Intersection) || !areIntersectedTypesAvoidingPrimitiveReduction((t as IntersectionType).types))
|
||||
) as TemplateLiteralType[];
|
||||
if (templates.length) {
|
||||
let i = types.length;
|
||||
while (i > 0) {
|
||||
@ -16966,8 +16970,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return reduceLeft(types, (n, t) => n + getConstituentCount(t), 0);
|
||||
}
|
||||
|
||||
function areIntersectedTypesAvoidingPrimitiveReduction(t1: Type, t2: Type) {
|
||||
return !!(t1.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) && t2 === emptyTypeLiteralType;
|
||||
function areIntersectedTypesAvoidingPrimitiveReduction(types: Type[], primitiveFlags = TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt): boolean {
|
||||
if (types.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
const [t1, t2] = types;
|
||||
return !!(t1.flags & primitiveFlags) && t2 === emptyTypeLiteralType || !!(t2.flags & primitiveFlags) && t1 === emptyTypeLiteralType;
|
||||
}
|
||||
|
||||
function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type {
|
||||
@ -16975,7 +16983,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (!links.resolvedType) {
|
||||
const aliasSymbol = getAliasSymbolForTypeNode(node);
|
||||
const types = map(node.types, getTypeFromTypeNode);
|
||||
const noSupertypeReduction = types.length === 2 && (areIntersectedTypesAvoidingPrimitiveReduction(types[0], types[1]) || areIntersectedTypesAvoidingPrimitiveReduction(types[1], types[0]));
|
||||
const noSupertypeReduction = areIntersectedTypesAvoidingPrimitiveReduction(types);
|
||||
links.resolvedType = getIntersectionType(types, aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol), noSupertypeReduction);
|
||||
}
|
||||
return links.resolvedType;
|
||||
@ -24362,12 +24370,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (source === target || target.flags & (TypeFlags.Any | TypeFlags.String)) {
|
||||
return true;
|
||||
}
|
||||
if (target.flags & TypeFlags.Intersection) {
|
||||
return every((target as IntersectionType).types, t => t === emptyTypeLiteralType || isValidTypeForTemplateLiteralPlaceholder(source, t));
|
||||
}
|
||||
if (source.flags & TypeFlags.StringLiteral) {
|
||||
const value = (source as StringLiteralType).value;
|
||||
return !!(target.flags & TypeFlags.Number && isValidNumberString(value, /*roundTripOnly*/ false) ||
|
||||
target.flags & TypeFlags.BigInt && isValidBigIntString(value, /*roundTripOnly*/ false) ||
|
||||
target.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) && value === (target as IntrinsicType).intrinsicName ||
|
||||
target.flags & TypeFlags.StringMapping && isMemberOfStringMapping(getStringLiteralType(value), target));
|
||||
target.flags & TypeFlags.StringMapping && isMemberOfStringMapping(getStringLiteralType(value), target) ||
|
||||
target.flags & TypeFlags.TemplateLiteral && isTypeMatchedByTemplateLiteralType(source, target as TemplateLiteralType));
|
||||
}
|
||||
if (source.flags & TypeFlags.TemplateLiteral) {
|
||||
const texts = (source as TemplateLiteralType).texts;
|
||||
|
||||
@ -55,9 +55,10 @@ templateLiteralTypesPatterns.ts(129,9): error TS2345: Argument of type '"1.1e-10
|
||||
templateLiteralTypesPatterns.ts(140,1): error TS2322: Type '`a${string}`' is not assignable to type '`a${number}`'.
|
||||
templateLiteralTypesPatterns.ts(141,1): error TS2322: Type '"bno"' is not assignable to type '`a${any}`'.
|
||||
templateLiteralTypesPatterns.ts(160,7): error TS2322: Type '"anything"' is not assignable to type '`${number} ${number}`'.
|
||||
templateLiteralTypesPatterns.ts(211,5): error TS2345: Argument of type '"abcTest"' is not assignable to parameter of type '`${`a${string}` & `${string}a`}Test`'.
|
||||
|
||||
|
||||
==== templateLiteralTypesPatterns.ts (57 errors) ====
|
||||
==== templateLiteralTypesPatterns.ts (58 errors) ====
|
||||
type RequiresLeadingSlash = `/${string}`;
|
||||
|
||||
// ok
|
||||
@ -373,4 +374,15 @@ templateLiteralTypesPatterns.ts(160,7): error TS2322: Type '"anything"' is not a
|
||||
this.get(id!);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/54177#issuecomment-1538436654
|
||||
function conversionTest(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${string & {}}Downcast`) {}
|
||||
conversionTest("testDowncast");
|
||||
function conversionTest2(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${{} & string}Downcast`) {}
|
||||
conversionTest2("testDowncast");
|
||||
|
||||
function foo(str: `${`a${string}` & `${string}a`}Test`) {}
|
||||
foo("abaTest"); // ok
|
||||
foo("abcTest"); // error
|
||||
~~~~~~~~~
|
||||
!!! error TS2345: Argument of type '"abcTest"' is not assignable to parameter of type '`${`a${string}` & `${string}a`}Test`'.
|
||||
@ -202,7 +202,16 @@ export abstract class BB {
|
||||
this.get(id!);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/54177#issuecomment-1538436654
|
||||
function conversionTest(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${string & {}}Downcast`) {}
|
||||
conversionTest("testDowncast");
|
||||
function conversionTest2(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${{} & string}Downcast`) {}
|
||||
conversionTest2("testDowncast");
|
||||
|
||||
function foo(str: `${`a${string}` & `${string}a`}Test`) {}
|
||||
foo("abaTest"); // ok
|
||||
foo("abcTest"); // error
|
||||
|
||||
//// [templateLiteralTypesPatterns.js]
|
||||
"use strict";
|
||||
@ -353,3 +362,11 @@ var BB = /** @class */ (function () {
|
||||
return BB;
|
||||
}());
|
||||
exports.BB = BB;
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/54177#issuecomment-1538436654
|
||||
function conversionTest(groupName) { }
|
||||
conversionTest("testDowncast");
|
||||
function conversionTest2(groupName) { }
|
||||
conversionTest2("testDowncast");
|
||||
function foo(str) { }
|
||||
foo("abaTest"); // ok
|
||||
foo("abcTest"); // error
|
||||
|
||||
@ -487,3 +487,28 @@ export abstract class BB {
|
||||
}
|
||||
}
|
||||
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/54177#issuecomment-1538436654
|
||||
function conversionTest(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${string & {}}Downcast`) {}
|
||||
>conversionTest : Symbol(conversionTest, Decl(templateLiteralTypesPatterns.ts, 200, 1))
|
||||
>groupName : Symbol(groupName, Decl(templateLiteralTypesPatterns.ts, 203, 24))
|
||||
|
||||
conversionTest("testDowncast");
|
||||
>conversionTest : Symbol(conversionTest, Decl(templateLiteralTypesPatterns.ts, 200, 1))
|
||||
|
||||
function conversionTest2(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${{} & string}Downcast`) {}
|
||||
>conversionTest2 : Symbol(conversionTest2, Decl(templateLiteralTypesPatterns.ts, 204, 31))
|
||||
>groupName : Symbol(groupName, Decl(templateLiteralTypesPatterns.ts, 205, 25))
|
||||
|
||||
conversionTest2("testDowncast");
|
||||
>conversionTest2 : Symbol(conversionTest2, Decl(templateLiteralTypesPatterns.ts, 204, 31))
|
||||
|
||||
function foo(str: `${`a${string}` & `${string}a`}Test`) {}
|
||||
>foo : Symbol(foo, Decl(templateLiteralTypesPatterns.ts, 206, 32))
|
||||
>str : Symbol(str, Decl(templateLiteralTypesPatterns.ts, 208, 13))
|
||||
|
||||
foo("abaTest"); // ok
|
||||
>foo : Symbol(foo, Decl(templateLiteralTypesPatterns.ts, 206, 32))
|
||||
|
||||
foo("abcTest"); // error
|
||||
>foo : Symbol(foo, Decl(templateLiteralTypesPatterns.ts, 206, 32))
|
||||
|
||||
|
||||
@ -635,3 +635,36 @@ export abstract class BB {
|
||||
}
|
||||
}
|
||||
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/54177#issuecomment-1538436654
|
||||
function conversionTest(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${string & {}}Downcast`) {}
|
||||
>conversionTest : (groupName: "downcast" | "dataDowncast" | "editingDowncast" | `${string & {}}Downcast`) => void
|
||||
>groupName : `${string & {}}Downcast` | "downcast" | "dataDowncast" | "editingDowncast"
|
||||
|
||||
conversionTest("testDowncast");
|
||||
>conversionTest("testDowncast") : void
|
||||
>conversionTest : (groupName: `${string & {}}Downcast` | "downcast" | "dataDowncast" | "editingDowncast") => void
|
||||
>"testDowncast" : "testDowncast"
|
||||
|
||||
function conversionTest2(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${{} & string}Downcast`) {}
|
||||
>conversionTest2 : (groupName: "downcast" | "dataDowncast" | "editingDowncast" | `${{} & string}Downcast`) => void
|
||||
>groupName : "downcast" | "dataDowncast" | "editingDowncast" | `${{} & string}Downcast`
|
||||
|
||||
conversionTest2("testDowncast");
|
||||
>conversionTest2("testDowncast") : void
|
||||
>conversionTest2 : (groupName: "downcast" | "dataDowncast" | "editingDowncast" | `${{} & string}Downcast`) => void
|
||||
>"testDowncast" : "testDowncast"
|
||||
|
||||
function foo(str: `${`a${string}` & `${string}a`}Test`) {}
|
||||
>foo : (str: `${`a${string}` & `${string}a`}Test`) => void
|
||||
>str : `${`a${string}` & `${string}a`}Test`
|
||||
|
||||
foo("abaTest"); // ok
|
||||
>foo("abaTest") : void
|
||||
>foo : (str: `${`a${string}` & `${string}a`}Test`) => void
|
||||
>"abaTest" : "abaTest"
|
||||
|
||||
foo("abcTest"); // error
|
||||
>foo("abcTest") : void
|
||||
>foo : (str: `${`a${string}` & `${string}a`}Test`) => void
|
||||
>"abcTest" : "abcTest"
|
||||
|
||||
|
||||
@ -200,3 +200,13 @@ export abstract class BB {
|
||||
this.get(id!);
|
||||
}
|
||||
}
|
||||
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/54177#issuecomment-1538436654
|
||||
function conversionTest(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${string & {}}Downcast`) {}
|
||||
conversionTest("testDowncast");
|
||||
function conversionTest2(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${{} & string}Downcast`) {}
|
||||
conversionTest2("testDowncast");
|
||||
|
||||
function foo(str: `${`a${string}` & `${string}a`}Test`) {}
|
||||
foo("abaTest"); // ok
|
||||
foo("abcTest"); // error
|
||||
@ -0,0 +1,6 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
//// function conversionTest(groupName: | "downcast" | "dataDowncast" | "editingDowncast" | `${string & {}}Downcast`) {}
|
||||
//// conversionTest("/**/");
|
||||
|
||||
verify.completions({ marker: "", exact: ["downcast", "dataDowncast", "editingDowncast"] });
|
||||
Loading…
x
Reference in New Issue
Block a user