Fixed an issue with reverse mapped types inference when single type variable is left after inferring from matching types (#55941)

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
Mateusz Burzyński 2023-11-30 23:57:52 +01:00 committed by GitHub
parent 30f3ce7b51
commit 6edfef8c0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 380 additions and 1 deletions

View File

@ -25354,7 +25354,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
target = getIntersectionType(targets);
}
}
else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
target = getActualTypeVariable(target);
}
if (target.flags & TypeFlags.TypeVariable) {

View File

@ -0,0 +1,172 @@
//// [tests/cases/compiler/reverseMappedUnionInference.ts] ////
=== reverseMappedUnionInference.ts ===
interface AnyExtractor<Result> {
>AnyExtractor : Symbol(AnyExtractor, Decl(reverseMappedUnionInference.ts, 0, 0))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 0, 23))
matches: (node: any) => boolean;
>matches : Symbol(AnyExtractor.matches, Decl(reverseMappedUnionInference.ts, 0, 32))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 1, 12))
extract: (node: any) => Result | undefined;
>extract : Symbol(AnyExtractor.extract, Decl(reverseMappedUnionInference.ts, 1, 34))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 2, 12))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 0, 23))
}
interface Extractor<T, Result> {
>Extractor : Symbol(Extractor, Decl(reverseMappedUnionInference.ts, 3, 1))
>T : Symbol(T, Decl(reverseMappedUnionInference.ts, 5, 20))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 5, 22))
matches: (node: unknown) => node is T;
>matches : Symbol(Extractor.matches, Decl(reverseMappedUnionInference.ts, 5, 32))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 6, 12))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 6, 12))
>T : Symbol(T, Decl(reverseMappedUnionInference.ts, 5, 20))
extract: (node: T) => Result | undefined;
>extract : Symbol(Extractor.extract, Decl(reverseMappedUnionInference.ts, 6, 40))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 7, 12))
>T : Symbol(T, Decl(reverseMappedUnionInference.ts, 5, 20))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 5, 22))
}
declare function createExtractor<T, Result>(params: {
>createExtractor : Symbol(createExtractor, Decl(reverseMappedUnionInference.ts, 8, 1))
>T : Symbol(T, Decl(reverseMappedUnionInference.ts, 10, 33))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 10, 35))
>params : Symbol(params, Decl(reverseMappedUnionInference.ts, 10, 44))
matcher: (node: unknown) => node is T;
>matcher : Symbol(matcher, Decl(reverseMappedUnionInference.ts, 10, 53))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 11, 12))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 11, 12))
>T : Symbol(T, Decl(reverseMappedUnionInference.ts, 10, 33))
extract: (node: T) => Result;
>extract : Symbol(extract, Decl(reverseMappedUnionInference.ts, 11, 40))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 12, 12))
>T : Symbol(T, Decl(reverseMappedUnionInference.ts, 10, 33))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 10, 35))
}): Extractor<T, Result>;
>Extractor : Symbol(Extractor, Decl(reverseMappedUnionInference.ts, 3, 1))
>T : Symbol(T, Decl(reverseMappedUnionInference.ts, 10, 33))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 10, 35))
interface Identifier {
>Identifier : Symbol(Identifier, Decl(reverseMappedUnionInference.ts, 13, 25))
kind: "identifier";
>kind : Symbol(Identifier.kind, Decl(reverseMappedUnionInference.ts, 15, 22))
name: string;
>name : Symbol(Identifier.name, Decl(reverseMappedUnionInference.ts, 16, 21))
}
declare function isIdentifier(node: unknown): node is Identifier;
>isIdentifier : Symbol(isIdentifier, Decl(reverseMappedUnionInference.ts, 18, 1))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 20, 30))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 20, 30))
>Identifier : Symbol(Identifier, Decl(reverseMappedUnionInference.ts, 13, 25))
const identifierExtractor = createExtractor({
>identifierExtractor : Symbol(identifierExtractor, Decl(reverseMappedUnionInference.ts, 22, 5))
>createExtractor : Symbol(createExtractor, Decl(reverseMappedUnionInference.ts, 8, 1))
matcher: isIdentifier,
>matcher : Symbol(matcher, Decl(reverseMappedUnionInference.ts, 22, 45))
>isIdentifier : Symbol(isIdentifier, Decl(reverseMappedUnionInference.ts, 18, 1))
extract: (node) => {
>extract : Symbol(extract, Decl(reverseMappedUnionInference.ts, 23, 24))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 24, 12))
return {
node,
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 25, 12))
kind: "identifier" as const,
>kind : Symbol(kind, Decl(reverseMappedUnionInference.ts, 26, 11))
>const : Symbol(const)
value: node.name,
>value : Symbol(value, Decl(reverseMappedUnionInference.ts, 27, 34))
>node.name : Symbol(Identifier.name, Decl(reverseMappedUnionInference.ts, 16, 21))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 24, 12))
>name : Symbol(Identifier.name, Decl(reverseMappedUnionInference.ts, 16, 21))
};
},
});
interface StringLiteral {
>StringLiteral : Symbol(StringLiteral, Decl(reverseMappedUnionInference.ts, 31, 3))
kind: "stringLiteral";
>kind : Symbol(StringLiteral.kind, Decl(reverseMappedUnionInference.ts, 33, 25))
value: string;
>value : Symbol(StringLiteral.value, Decl(reverseMappedUnionInference.ts, 34, 24))
}
declare function isStringLiteral(node: unknown): node is StringLiteral;
>isStringLiteral : Symbol(isStringLiteral, Decl(reverseMappedUnionInference.ts, 36, 1))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 38, 33))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 38, 33))
>StringLiteral : Symbol(StringLiteral, Decl(reverseMappedUnionInference.ts, 31, 3))
const stringExtractor = createExtractor({
>stringExtractor : Symbol(stringExtractor, Decl(reverseMappedUnionInference.ts, 40, 5))
>createExtractor : Symbol(createExtractor, Decl(reverseMappedUnionInference.ts, 8, 1))
matcher: isStringLiteral,
>matcher : Symbol(matcher, Decl(reverseMappedUnionInference.ts, 40, 41))
>isStringLiteral : Symbol(isStringLiteral, Decl(reverseMappedUnionInference.ts, 36, 1))
extract: (node) => {
>extract : Symbol(extract, Decl(reverseMappedUnionInference.ts, 41, 27))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 42, 12))
return {
node,
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 43, 12))
kind: "string" as const,
>kind : Symbol(kind, Decl(reverseMappedUnionInference.ts, 44, 11))
>const : Symbol(const)
value: node.value,
>value : Symbol(value, Decl(reverseMappedUnionInference.ts, 45, 30))
>node.value : Symbol(StringLiteral.value, Decl(reverseMappedUnionInference.ts, 34, 24))
>node : Symbol(node, Decl(reverseMappedUnionInference.ts, 42, 12))
>value : Symbol(StringLiteral.value, Decl(reverseMappedUnionInference.ts, 34, 24))
};
},
});
declare function unionType<Result extends readonly unknown[]>(parsers: {
>unionType : Symbol(unionType, Decl(reverseMappedUnionInference.ts, 49, 3))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 51, 27))
>parsers : Symbol(parsers, Decl(reverseMappedUnionInference.ts, 51, 62))
[K in keyof Result]: AnyExtractor<Result[K]>;
>K : Symbol(K, Decl(reverseMappedUnionInference.ts, 52, 3))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 51, 27))
>AnyExtractor : Symbol(AnyExtractor, Decl(reverseMappedUnionInference.ts, 0, 0))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 51, 27))
>K : Symbol(K, Decl(reverseMappedUnionInference.ts, 52, 3))
}): AnyExtractor<Result[number]>;
>AnyExtractor : Symbol(AnyExtractor, Decl(reverseMappedUnionInference.ts, 0, 0))
>Result : Symbol(Result, Decl(reverseMappedUnionInference.ts, 51, 27))
const myUnion = unionType([identifierExtractor, stringExtractor]);
>myUnion : Symbol(myUnion, Decl(reverseMappedUnionInference.ts, 55, 5))
>unionType : Symbol(unionType, Decl(reverseMappedUnionInference.ts, 49, 3))
>identifierExtractor : Symbol(identifierExtractor, Decl(reverseMappedUnionInference.ts, 22, 5))
>stringExtractor : Symbol(stringExtractor, Decl(reverseMappedUnionInference.ts, 40, 5))

View File

@ -0,0 +1,148 @@
//// [tests/cases/compiler/reverseMappedUnionInference.ts] ////
=== reverseMappedUnionInference.ts ===
interface AnyExtractor<Result> {
matches: (node: any) => boolean;
>matches : (node: any) => boolean
>node : any
extract: (node: any) => Result | undefined;
>extract : (node: any) => Result | undefined
>node : any
}
interface Extractor<T, Result> {
matches: (node: unknown) => node is T;
>matches : (node: unknown) => node is T
>node : unknown
extract: (node: T) => Result | undefined;
>extract : (node: T) => Result | undefined
>node : T
}
declare function createExtractor<T, Result>(params: {
>createExtractor : <T, Result>(params: { matcher: (node: unknown) => node is T; extract: (node: T) => Result; }) => Extractor<T, Result>
>params : { matcher: (node: unknown) => node is T; extract: (node: T) => Result; }
matcher: (node: unknown) => node is T;
>matcher : (node: unknown) => node is T
>node : unknown
extract: (node: T) => Result;
>extract : (node: T) => Result
>node : T
}): Extractor<T, Result>;
interface Identifier {
kind: "identifier";
>kind : "identifier"
name: string;
>name : string
}
declare function isIdentifier(node: unknown): node is Identifier;
>isIdentifier : (node: unknown) => node is Identifier
>node : unknown
const identifierExtractor = createExtractor({
>identifierExtractor : Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }>
>createExtractor({ matcher: isIdentifier, extract: (node) => { return { node, kind: "identifier" as const, value: node.name, }; },}) : Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }>
>createExtractor : <T, Result>(params: { matcher: (node: unknown) => node is T; extract: (node: T) => Result; }) => Extractor<T, Result>
>{ matcher: isIdentifier, extract: (node) => { return { node, kind: "identifier" as const, value: node.name, }; },} : { matcher: (node: unknown) => node is Identifier; extract: (node: Identifier) => { node: Identifier; kind: "identifier"; value: string; }; }
matcher: isIdentifier,
>matcher : (node: unknown) => node is Identifier
>isIdentifier : (node: unknown) => node is Identifier
extract: (node) => {
>extract : (node: Identifier) => { node: Identifier; kind: "identifier"; value: string; }
>(node) => { return { node, kind: "identifier" as const, value: node.name, }; } : (node: Identifier) => { node: Identifier; kind: "identifier"; value: string; }
>node : Identifier
return {
>{ node, kind: "identifier" as const, value: node.name, } : { node: Identifier; kind: "identifier"; value: string; }
node,
>node : Identifier
kind: "identifier" as const,
>kind : "identifier"
>"identifier" as const : "identifier"
>"identifier" : "identifier"
value: node.name,
>value : string
>node.name : string
>node : Identifier
>name : string
};
},
});
interface StringLiteral {
kind: "stringLiteral";
>kind : "stringLiteral"
value: string;
>value : string
}
declare function isStringLiteral(node: unknown): node is StringLiteral;
>isStringLiteral : (node: unknown) => node is StringLiteral
>node : unknown
const stringExtractor = createExtractor({
>stringExtractor : Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>
>createExtractor({ matcher: isStringLiteral, extract: (node) => { return { node, kind: "string" as const, value: node.value, }; },}) : Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>
>createExtractor : <T, Result>(params: { matcher: (node: unknown) => node is T; extract: (node: T) => Result; }) => Extractor<T, Result>
>{ matcher: isStringLiteral, extract: (node) => { return { node, kind: "string" as const, value: node.value, }; },} : { matcher: (node: unknown) => node is StringLiteral; extract: (node: StringLiteral) => { node: StringLiteral; kind: "string"; value: string; }; }
matcher: isStringLiteral,
>matcher : (node: unknown) => node is StringLiteral
>isStringLiteral : (node: unknown) => node is StringLiteral
extract: (node) => {
>extract : (node: StringLiteral) => { node: StringLiteral; kind: "string"; value: string; }
>(node) => { return { node, kind: "string" as const, value: node.value, }; } : (node: StringLiteral) => { node: StringLiteral; kind: "string"; value: string; }
>node : StringLiteral
return {
>{ node, kind: "string" as const, value: node.value, } : { node: StringLiteral; kind: "string"; value: string; }
node,
>node : StringLiteral
kind: "string" as const,
>kind : "string"
>"string" as const : "string"
>"string" : "string"
value: node.value,
>value : string
>node.value : string
>node : StringLiteral
>value : string
};
},
});
declare function unionType<Result extends readonly unknown[]>(parsers: {
>unionType : <Result extends readonly unknown[]>(parsers: { [K in keyof Result]: AnyExtractor<Result[K]>; }) => AnyExtractor<Result[number]>
>parsers : { [K in keyof Result]: AnyExtractor<Result[K]>; }
[K in keyof Result]: AnyExtractor<Result[K]>;
}): AnyExtractor<Result[number]>;
const myUnion = unionType([identifierExtractor, stringExtractor]);
>myUnion : AnyExtractor<{ node: Identifier; kind: "identifier"; value: string; } | { node: StringLiteral; kind: "string"; value: string; }>
>unionType([identifierExtractor, stringExtractor]) : AnyExtractor<{ node: Identifier; kind: "identifier"; value: string; } | { node: StringLiteral; kind: "string"; value: string; }>
>unionType : <Result extends readonly unknown[]>(parsers: { [K in keyof Result]: AnyExtractor<Result[K]>; }) => AnyExtractor<Result[number]>
>[identifierExtractor, stringExtractor] : (Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }> | Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>)[]
>identifierExtractor : Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }>
>stringExtractor : Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>

View File

@ -0,0 +1,59 @@
// @strict: true
// @noEmit: true
interface AnyExtractor<Result> {
matches: (node: any) => boolean;
extract: (node: any) => Result | undefined;
}
interface Extractor<T, Result> {
matches: (node: unknown) => node is T;
extract: (node: T) => Result | undefined;
}
declare function createExtractor<T, Result>(params: {
matcher: (node: unknown) => node is T;
extract: (node: T) => Result;
}): Extractor<T, Result>;
interface Identifier {
kind: "identifier";
name: string;
}
declare function isIdentifier(node: unknown): node is Identifier;
const identifierExtractor = createExtractor({
matcher: isIdentifier,
extract: (node) => {
return {
node,
kind: "identifier" as const,
value: node.name,
};
},
});
interface StringLiteral {
kind: "stringLiteral";
value: string;
}
declare function isStringLiteral(node: unknown): node is StringLiteral;
const stringExtractor = createExtractor({
matcher: isStringLiteral,
extract: (node) => {
return {
node,
kind: "string" as const,
value: node.value,
};
},
});
declare function unionType<Result extends readonly unknown[]>(parsers: {
[K in keyof Result]: AnyExtractor<Result[K]>;
}): AnyExtractor<Result[number]>;
const myUnion = unionType([identifierExtractor, stringExtractor]);