Description
TypeScript Version:
originally seen in 2.9.2
reproduced in 3.1.0-dev.20180724
Search Terms:
Code
export interface Program {
type: 'Program'
body: AST[]
}
export interface MemberExpression {
type: 'MemberExpression'
object: AST
property: AST
}
export interface FunctionDeclaration {
type: 'FunctionDeclaration'
body: AST
}
export type AST =
| Program
| MemberExpression
| FunctionDeclaration
type ASTKeys<T extends AST> = {
[K in keyof T]: T[K] extends AST ? K : T[K] extends AST[] ? K : never
}[keyof T]
export type PickASTKeys<T extends AST> = Pick<T, ASTKeys<T>>
function pickASTProperties<T extends AST>(ast: AST): PickASTKeys<T> {
switch (ast.type) {
case 'FunctionDeclaration': return {body: ast.body}
case 'Program': return { body: ast.body }
case 'MemberExpression': return { object: ast.object, property: ast.property }
}
}
Expected behavior:
This code should compile. The union type should be narrowed down by the switch statement and the returned type should behave correctly. Interestingly, this compilation error can be worked around. If you supply a lookup that looks like the below and update the function declaration, the expected behavior does occur:
export interface Lookup {
Program: PickASTKeys<Program>;
MemberExpression: PickASTKeys<MemberExpression>;
FunctionDeclaration: PickASTKeys<FunctionDeclaration>;
}
function pickASTProperties<T extends AST>(ast: AST): Lookup[T['type']] {/*... same code*/}
However, this workaround only works if the PickASTKeys is there, if I do not include the PickASTKeys in the lookup and put that in the return type, I again get compilation errors. I have included that example in the playground list
Actual behavior:
3 errors are received:
index.ts(33,7): error TS2322: Type '{ body: AST; }' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends AST ? K : T[K] extends AST[] ? K : never; }[keyof T]>'.
index.ts(35,7): error TS2322: Type '{ body: AST[]; }' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends AST ? K : T[K] extends AST[] ? K : never; }[keyof T]>'.
index.ts(37,7): error TS2322: Type '{ object: AST; property: AST; }' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends AST ? K : T[K] extends AST[] ? K : never; }[keyof T]>'.
Playground Link:
Link with failing compiler
Link with workaround that does not compile
Link with workaround
Related Issues:
I honestly looked, but the namespace for this type of issue is fairly overloaded. I would not find anything within a reasonable timeframe, but I might have been looking for the wrong keywords.