Resolve contextual computed properties with non-bindable names (#51915)

This commit is contained in:
Mateusz Burzyński 2023-03-01 19:12:18 +01:00 committed by GitHub
parent 73a98aee00
commit 58ee54c2aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 243 additions and 0 deletions

View File

@ -28997,6 +28997,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const symbol = getSymbolOfDeclaration(element);
return getTypeOfPropertyOfContextualType(type, symbol.escapedName, getSymbolLinks(symbol).nameType);
}
if (hasDynamicName(element)) {
const name = getNameOfDeclaration(element);
if (name && isComputedPropertyName(name)) {
const exprType = checkExpression(name.expression);
const propType = isTypeUsableAsPropertyName(exprType) && getTypeOfPropertyOfContextualType(type, getPropertyNameFromType(exprType));
if (propType) {
return propType;
}
}
}
if (element.name) {
const nameType = getLiteralTypeFromPropertyName(element.name);
// We avoid calling getApplicableIndexInfo here because it performs potentially expensive intersection reduction.

View File

@ -0,0 +1,98 @@
=== tests/cases/compiler/contextualComputedNonBindablePropertyType.ts ===
// repro #51906
declare function testD(): "d";
>testD : Symbol(testD, Decl(contextualComputedNonBindablePropertyType.ts, 0, 0))
declare function forceMatch<T>(matched: {
>forceMatch : Symbol(forceMatch, Decl(contextualComputedNonBindablePropertyType.ts, 2, 30))
>T : Symbol(T, Decl(contextualComputedNonBindablePropertyType.ts, 4, 28))
>matched : Symbol(matched, Decl(contextualComputedNonBindablePropertyType.ts, 4, 31))
[key in keyof T]: key;
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 5, 3))
>T : Symbol(T, Decl(contextualComputedNonBindablePropertyType.ts, 4, 28))
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 5, 3))
}): void;
forceMatch({
>forceMatch : Symbol(forceMatch, Decl(contextualComputedNonBindablePropertyType.ts, 2, 30))
[testD()]: "d",
>[testD()] : Symbol([testD()], Decl(contextualComputedNonBindablePropertyType.ts, 8, 12))
>testD : Symbol(testD, Decl(contextualComputedNonBindablePropertyType.ts, 0, 0))
});
declare function forceMatch2<T>(matched: {
>forceMatch2 : Symbol(forceMatch2, Decl(contextualComputedNonBindablePropertyType.ts, 10, 3))
>T : Symbol(T, Decl(contextualComputedNonBindablePropertyType.ts, 12, 29))
>matched : Symbol(matched, Decl(contextualComputedNonBindablePropertyType.ts, 12, 32))
[key in keyof T]: ({ key }: { key: key }) => void;
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 13, 3))
>T : Symbol(T, Decl(contextualComputedNonBindablePropertyType.ts, 12, 29))
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 13, 22))
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 13, 31))
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 13, 3))
}): void;
forceMatch2({
>forceMatch2 : Symbol(forceMatch2, Decl(contextualComputedNonBindablePropertyType.ts, 10, 3))
[testD()]: ({ key }) => {},
>[testD()] : Symbol([testD()], Decl(contextualComputedNonBindablePropertyType.ts, 16, 13))
>testD : Symbol(testD, Decl(contextualComputedNonBindablePropertyType.ts, 0, 0))
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 17, 15))
});
// repro #52954
type Original = { foo: 'expects a string literal', baz: boolean, bar: number }
>Original : Symbol(Original, Decl(contextualComputedNonBindablePropertyType.ts, 18, 3))
>foo : Symbol(foo, Decl(contextualComputedNonBindablePropertyType.ts, 22, 17))
>baz : Symbol(baz, Decl(contextualComputedNonBindablePropertyType.ts, 22, 50))
>bar : Symbol(bar, Decl(contextualComputedNonBindablePropertyType.ts, 22, 64))
type Mapped = {
>Mapped : Symbol(Mapped, Decl(contextualComputedNonBindablePropertyType.ts, 22, 78))
[prop in keyof Original]: (arg: Original[prop]) => Original[prop]
>prop : Symbol(prop, Decl(contextualComputedNonBindablePropertyType.ts, 25, 3))
>Original : Symbol(Original, Decl(contextualComputedNonBindablePropertyType.ts, 18, 3))
>arg : Symbol(arg, Decl(contextualComputedNonBindablePropertyType.ts, 25, 29))
>Original : Symbol(Original, Decl(contextualComputedNonBindablePropertyType.ts, 18, 3))
>prop : Symbol(prop, Decl(contextualComputedNonBindablePropertyType.ts, 25, 3))
>Original : Symbol(Original, Decl(contextualComputedNonBindablePropertyType.ts, 18, 3))
>prop : Symbol(prop, Decl(contextualComputedNonBindablePropertyType.ts, 25, 3))
}
const propSelector = <propName extends string>(propName: propName): propName => propName;
>propSelector : Symbol(propSelector, Decl(contextualComputedNonBindablePropertyType.ts, 28, 5))
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
const unexpectedlyFailingExample: Mapped = {
>unexpectedlyFailingExample : Symbol(unexpectedlyFailingExample, Decl(contextualComputedNonBindablePropertyType.ts, 30, 5))
>Mapped : Symbol(Mapped, Decl(contextualComputedNonBindablePropertyType.ts, 22, 78))
foo: (arg) => 'expects a string literal',
>foo : Symbol(foo, Decl(contextualComputedNonBindablePropertyType.ts, 30, 44))
>arg : Symbol(arg, Decl(contextualComputedNonBindablePropertyType.ts, 31, 8))
baz: (arg) => true,
>baz : Symbol(baz, Decl(contextualComputedNonBindablePropertyType.ts, 31, 43))
>arg : Symbol(arg, Decl(contextualComputedNonBindablePropertyType.ts, 32, 8))
[propSelector('bar')]: (arg) => 51345
>[propSelector('bar')] : Symbol([propSelector('bar')], Decl(contextualComputedNonBindablePropertyType.ts, 32, 21))
>propSelector : Symbol(propSelector, Decl(contextualComputedNonBindablePropertyType.ts, 28, 5))
>arg : Symbol(arg, Decl(contextualComputedNonBindablePropertyType.ts, 33, 26))
}

View File

@ -0,0 +1,97 @@
=== tests/cases/compiler/contextualComputedNonBindablePropertyType.ts ===
// repro #51906
declare function testD(): "d";
>testD : () => "d"
declare function forceMatch<T>(matched: {
>forceMatch : <T>(matched: { [key in keyof T]: key; }) => void
>matched : { [key in keyof T]: key; }
[key in keyof T]: key;
}): void;
forceMatch({
>forceMatch({ [testD()]: "d",}) : void
>forceMatch : <T>(matched: { [key in keyof T]: key; }) => void
>{ [testD()]: "d",} : { d: "d"; }
[testD()]: "d",
>[testD()] : "d"
>testD() : "d"
>testD : () => "d"
>"d" : "d"
});
declare function forceMatch2<T>(matched: {
>forceMatch2 : <T>(matched: { [key in keyof T]: ({ key }: { key: key; }) => void; }) => void
>matched : { [key in keyof T]: ({ key }: { key: key; }) => void; }
[key in keyof T]: ({ key }: { key: key }) => void;
>key : key
>key : key
}): void;
forceMatch2({
>forceMatch2({ [testD()]: ({ key }) => {},}) : void
>forceMatch2 : <T>(matched: { [key in keyof T]: ({ key }: { key: key; }) => void; }) => void
>{ [testD()]: ({ key }) => {},} : { d: ({ key }: { key: "d"; }) => void; }
[testD()]: ({ key }) => {},
>[testD()] : ({ key }: { key: "d"; }) => void
>testD() : "d"
>testD : () => "d"
>({ key }) => {} : ({ key }: { key: "d"; }) => void
>key : "d"
});
// repro #52954
type Original = { foo: 'expects a string literal', baz: boolean, bar: number }
>Original : { foo: 'expects a string literal'; baz: boolean; bar: number; }
>foo : "expects a string literal"
>baz : boolean
>bar : number
type Mapped = {
>Mapped : { foo: (arg: "expects a string literal") => "expects a string literal"; baz: (arg: boolean) => boolean; bar: (arg: number) => number; }
[prop in keyof Original]: (arg: Original[prop]) => Original[prop]
>arg : Original[prop]
}
const propSelector = <propName extends string>(propName: propName): propName => propName;
>propSelector : <propName extends string>(propName: propName) => propName
><propName extends string>(propName: propName): propName => propName : <propName extends string>(propName: propName) => propName
>propName : propName
>propName : propName
const unexpectedlyFailingExample: Mapped = {
>unexpectedlyFailingExample : Mapped
>{ foo: (arg) => 'expects a string literal', baz: (arg) => true, [propSelector('bar')]: (arg) => 51345} : { foo: (arg: "expects a string literal") => "expects a string literal"; baz: (arg: boolean) => true; bar: (arg: number) => number; }
foo: (arg) => 'expects a string literal',
>foo : (arg: "expects a string literal") => "expects a string literal"
>(arg) => 'expects a string literal' : (arg: "expects a string literal") => "expects a string literal"
>arg : "expects a string literal"
>'expects a string literal' : "expects a string literal"
baz: (arg) => true,
>baz : (arg: boolean) => true
>(arg) => true : (arg: boolean) => true
>arg : boolean
>true : true
[propSelector('bar')]: (arg) => 51345
>[propSelector('bar')] : (arg: number) => number
>propSelector('bar') : "bar"
>propSelector : <propName extends string>(propName: propName) => propName
>'bar' : "bar"
>(arg) => 51345 : (arg: number) => number
>arg : number
>51345 : 51345
}

View File

@ -0,0 +1,38 @@
// @noEmit: true
// @strict: true
// repro #51906
declare function testD(): "d";
declare function forceMatch<T>(matched: {
[key in keyof T]: key;
}): void;
forceMatch({
[testD()]: "d",
});
declare function forceMatch2<T>(matched: {
[key in keyof T]: ({ key }: { key: key }) => void;
}): void;
forceMatch2({
[testD()]: ({ key }) => {},
});
// repro #52954
type Original = { foo: 'expects a string literal', baz: boolean, bar: number }
type Mapped = {
[prop in keyof Original]: (arg: Original[prop]) => Original[prop]
}
const propSelector = <propName extends string>(propName: propName): propName => propName;
const unexpectedlyFailingExample: Mapped = {
foo: (arg) => 'expects a string literal',
baz: (arg) => true,
[propSelector('bar')]: (arg) => 51345
}