Skip to content

Commit 2c50458

Browse files
TypeScript BotdragomirtitianDanielRosenwasser
authored
🤖 Pick PR microsoft#58758 (Resolve keyof and index operations ...) into release-5.5 (microsoft#58767)
Co-authored-by: Titian Cernicova-Dragomir <[email protected]> Co-authored-by: Daniel Rosenwasser <[email protected]>
1 parent 1065222 commit 2c50458

File tree

5 files changed

+276
-23
lines changed

5 files changed

+276
-23
lines changed

‎src/compiler/checker.ts

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8601,7 +8601,58 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
86018601
return enterNewScope(context, node, getParametersInScope(node), getTypeParametersInScope(node));
86028602
}
86038603

8604-
function tryVisitTypeReference(node: TypeReferenceNode) {
8604+
function tryVisitSimpleTypeNode(node: TypeNode): TypeNode | undefined {
8605+
const innerNode = skipTypeParentheses(node);
8606+
switch (innerNode.kind) {
8607+
case SyntaxKind.TypeReference:
8608+
return tryVisitTypeReference(innerNode as TypeReferenceNode);
8609+
case SyntaxKind.TypeQuery:
8610+
return tryVisitTypeQuery(innerNode as TypeQueryNode);
8611+
case SyntaxKind.IndexedAccessType:
8612+
return tryVisitIndexedAccess(innerNode as IndexedAccessTypeNode);
8613+
case SyntaxKind.TypeOperator:
8614+
const typeOperatorNode = innerNode as TypeOperatorNode;
8615+
if (typeOperatorNode.operator === SyntaxKind.KeyOfKeyword) {
8616+
return tryVisitKeyOf(typeOperatorNode);
8617+
}
8618+
}
8619+
return visitNode(node, visitExistingNodeTreeSymbols, isTypeNode);
8620+
}
8621+
8622+
function tryVisitIndexedAccess(node: IndexedAccessTypeNode): TypeNode | undefined {
8623+
const resultObjectType = tryVisitSimpleTypeNode(node.objectType);
8624+
if (resultObjectType === undefined) {
8625+
return undefined;
8626+
}
8627+
return factory.updateIndexedAccessTypeNode(node, resultObjectType, visitNode(node.indexType, visitExistingNodeTreeSymbols, isTypeNode)!);
8628+
}
8629+
8630+
function tryVisitKeyOf(node: TypeOperatorNode): TypeNode | undefined {
8631+
Debug.assertEqual(node.operator, SyntaxKind.KeyOfKeyword);
8632+
const type = tryVisitSimpleTypeNode(node.type);
8633+
if (type === undefined) {
8634+
return undefined;
8635+
}
8636+
return factory.updateTypeOperatorNode(node, type);
8637+
}
8638+
8639+
function tryVisitTypeQuery(node: TypeQueryNode): TypeNode | undefined {
8640+
const { introducesError, node: exprName } = trackExistingEntityName(node.exprName, context);
8641+
if (!introducesError) {
8642+
return factory.updateTypeQueryNode(
8643+
node,
8644+
exprName,
8645+
visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode),
8646+
);
8647+
}
8648+
8649+
const serializedName = serializeTypeName(context, node.exprName, /*isTypeOf*/ true);
8650+
if (serializedName) {
8651+
return setTextRange(context, serializedName, node.exprName);
8652+
}
8653+
}
8654+
8655+
function tryVisitTypeReference(node: TypeReferenceNode): TypeNode | undefined {
86058656
if (canReuseTypeNode(context, node)) {
86068657
const { introducesError, node: newName } = trackExistingEntityName(node.typeName, context);
86078658
const typeArguments = visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode);
@@ -8728,13 +8779,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
87288779
);
87298780
}
87308781

8731-
if (isIndexedAccessTypeNode(node) && isTypeReferenceNode(node.objectType)) {
8732-
const objectType = tryVisitTypeReference(node.objectType);
8733-
if (!objectType) {
8782+
if (isIndexedAccessTypeNode(node)) {
8783+
const result = tryVisitIndexedAccess(node);
8784+
if (!result) {
87348785
hadError = true;
87358786
return node;
87368787
}
8737-
return factory.updateIndexedAccessTypeNode(node, objectType, visitNode(node.indexType, visitExistingNodeTreeSymbols, isTypeNode)!);
8788+
return result;
87388789
}
87398790

87408791
if (isTypeReferenceNode(node)) {
@@ -8790,20 +8841,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
87908841
return visited;
87918842
}
87928843
if (isTypeQueryNode(node)) {
8793-
const { introducesError, node: exprName } = trackExistingEntityName(node.exprName, context);
8794-
if (introducesError) {
8795-
const serializedName = serializeTypeName(context, node.exprName, /*isTypeOf*/ true);
8796-
if (serializedName) {
8797-
return setTextRange(context, serializedName, node.exprName);
8798-
}
8844+
const result = tryVisitTypeQuery(node);
8845+
if (!result) {
87998846
hadError = true;
88008847
return node;
88018848
}
8802-
return factory.updateTypeQueryNode(
8803-
node,
8804-
exprName,
8805-
visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode),
8806-
);
8849+
return result;
88078850
}
88088851
if (isComputedPropertyName(node) && isEntityNameExpression(node.expression)) {
88098852
const { node: result, introducesError } = trackExistingEntityName(node.expression, context);
@@ -8877,14 +8920,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
88778920
}
88788921
}
88798922
else if (node.operator === SyntaxKind.KeyOfKeyword) {
8880-
if (isTypeReferenceNode(node.type)) {
8881-
const type = tryVisitTypeReference(node.type);
8882-
if (!type) {
8883-
hadError = true;
8884-
return node;
8885-
}
8886-
return factory.updateTypeOperatorNode(node, type);
8923+
const result = tryVisitKeyOf(node);
8924+
if (!result) {
8925+
hadError = true;
8926+
return node;
88878927
}
8928+
return result;
88888929
}
88898930
}
88908931

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//// [tests/cases/compiler/declarationEmitResolveTypesIfNotReusable.ts] ////
2+
3+
//// [decl.ts]
4+
const u = "X";
5+
type A = { a: { b : "value of b", notNecessary: typeof u }}
6+
const a = { a: "value of a", notNecessary: u } as const
7+
8+
9+
export const o1 = (o: A['a']['b']) => {}
10+
11+
export const o2 = (o: (typeof a)['a']) => {}
12+
export const o3 = (o: typeof a['a']) => {}
13+
14+
export const o4 = (o: keyof (A['a'])) => {}
15+
16+
//// [main.ts]
17+
import * as d from './decl'
18+
19+
export const f = {...d}
20+
21+
//// [decl.js]
22+
const u = "X";
23+
const a = { a: "value of a", notNecessary: u };
24+
export const o1 = (o) => { };
25+
export const o2 = (o) => { };
26+
export const o3 = (o) => { };
27+
export const o4 = (o) => { };
28+
//// [main.js]
29+
import * as d from './decl';
30+
export const f = { ...d };
31+
32+
33+
//// [decl.d.ts]
34+
declare const u = "X";
35+
type A = {
36+
a: {
37+
b: "value of b";
38+
notNecessary: typeof u;
39+
};
40+
};
41+
declare const a: {
42+
readonly a: "value of a";
43+
readonly notNecessary: "X";
44+
};
45+
export declare const o1: (o: A["a"]["b"]) => void;
46+
export declare const o2: (o: (typeof a)["a"]) => void;
47+
export declare const o3: (o: (typeof a)["a"]) => void;
48+
export declare const o4: (o: keyof A["a"]) => void;
49+
export {};
50+
//// [main.d.ts]
51+
export declare const f: {
52+
o1: (o: "value of b") => void;
53+
o2: (o: "value of a") => void;
54+
o3: (o: "value of a") => void;
55+
o4: (o: "b" | "notNecessary") => void;
56+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//// [tests/cases/compiler/declarationEmitResolveTypesIfNotReusable.ts] ////
2+
3+
=== decl.ts ===
4+
const u = "X";
5+
>u : Symbol(u, Decl(decl.ts, 0, 5))
6+
7+
type A = { a: { b : "value of b", notNecessary: typeof u }}
8+
>A : Symbol(A, Decl(decl.ts, 0, 14))
9+
>a : Symbol(a, Decl(decl.ts, 1, 10))
10+
>b : Symbol(b, Decl(decl.ts, 1, 15))
11+
>notNecessary : Symbol(notNecessary, Decl(decl.ts, 1, 33))
12+
>u : Symbol(u, Decl(decl.ts, 0, 5))
13+
14+
const a = { a: "value of a", notNecessary: u } as const
15+
>a : Symbol(a, Decl(decl.ts, 2, 5))
16+
>a : Symbol(a, Decl(decl.ts, 2, 11))
17+
>notNecessary : Symbol(notNecessary, Decl(decl.ts, 2, 28))
18+
>u : Symbol(u, Decl(decl.ts, 0, 5))
19+
>const : Symbol(const)
20+
21+
22+
export const o1 = (o: A['a']['b']) => {}
23+
>o1 : Symbol(o1, Decl(decl.ts, 5, 12))
24+
>o : Symbol(o, Decl(decl.ts, 5, 19))
25+
>A : Symbol(A, Decl(decl.ts, 0, 14))
26+
27+
export const o2 = (o: (typeof a)['a']) => {}
28+
>o2 : Symbol(o2, Decl(decl.ts, 7, 12))
29+
>o : Symbol(o, Decl(decl.ts, 7, 19))
30+
>a : Symbol(a, Decl(decl.ts, 2, 5))
31+
32+
export const o3 = (o: typeof a['a']) => {}
33+
>o3 : Symbol(o3, Decl(decl.ts, 8, 12))
34+
>o : Symbol(o, Decl(decl.ts, 8, 19))
35+
>a : Symbol(a, Decl(decl.ts, 2, 5))
36+
37+
export const o4 = (o: keyof (A['a'])) => {}
38+
>o4 : Symbol(o4, Decl(decl.ts, 10, 12))
39+
>o : Symbol(o, Decl(decl.ts, 10, 19))
40+
>A : Symbol(A, Decl(decl.ts, 0, 14))
41+
42+
=== main.ts ===
43+
import * as d from './decl'
44+
>d : Symbol(d, Decl(main.ts, 0, 6))
45+
46+
export const f = {...d}
47+
>f : Symbol(f, Decl(main.ts, 2, 12))
48+
>d : Symbol(d, Decl(main.ts, 0, 6))
49+
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//// [tests/cases/compiler/declarationEmitResolveTypesIfNotReusable.ts] ////
2+
3+
=== decl.ts ===
4+
const u = "X";
5+
>u : "X"
6+
> : ^^^
7+
>"X" : "X"
8+
> : ^^^
9+
10+
type A = { a: { b : "value of b", notNecessary: typeof u }}
11+
>A : A
12+
> : ^
13+
>a : { b: "value of b"; notNecessary: typeof u; }
14+
> : ^^^^^ ^^^^^^^^^^^^^^^^ ^^^
15+
>b : "value of b"
16+
> : ^^^^^^^^^^^^
17+
>notNecessary : "X"
18+
> : ^^^
19+
>u : "X"
20+
> : ^^^
21+
22+
const a = { a: "value of a", notNecessary: u } as const
23+
>a : { readonly a: "value of a"; readonly notNecessary: "X"; }
24+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
>{ a: "value of a", notNecessary: u } as const : { readonly a: "value of a"; readonly notNecessary: "X"; }
26+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27+
>{ a: "value of a", notNecessary: u } : { readonly a: "value of a"; readonly notNecessary: "X"; }
28+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29+
>a : "value of a"
30+
> : ^^^^^^^^^^^^
31+
>"value of a" : "value of a"
32+
> : ^^^^^^^^^^^^
33+
>notNecessary : "X"
34+
> : ^^^
35+
>u : "X"
36+
> : ^^^
37+
38+
39+
export const o1 = (o: A['a']['b']) => {}
40+
>o1 : (o: A["a"]["b"]) => void
41+
> : ^ ^^ ^^^^^^^^^
42+
>(o: A['a']['b']) => {} : (o: A["a"]["b"]) => void
43+
> : ^ ^^ ^^^^^^^^^
44+
>o : "value of b"
45+
> : ^^^^^^^^^^^^
46+
47+
export const o2 = (o: (typeof a)['a']) => {}
48+
>o2 : (o: (typeof a)["a"]) => void
49+
> : ^ ^^^ ^ ^^^^^^^^^
50+
>(o: (typeof a)['a']) => {} : (o: (typeof a)["a"]) => void
51+
> : ^ ^^^ ^ ^^^^^^^^^
52+
>o : "value of a"
53+
> : ^^^^^^^^^^^^
54+
>a : { readonly a: "value of a"; readonly notNecessary: "X"; }
55+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
57+
export const o3 = (o: typeof a['a']) => {}
58+
>o3 : (o: (typeof a)["a"]) => void
59+
> : ^ ^^^ ^ ^^^^^^^^^
60+
>(o: typeof a['a']) => {} : (o: (typeof a)["a"]) => void
61+
> : ^ ^^^ ^ ^^^^^^^^^
62+
>o : "value of a"
63+
> : ^^^^^^^^^^^^
64+
>a : { readonly a: "value of a"; readonly notNecessary: "X"; }
65+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66+
67+
export const o4 = (o: keyof (A['a'])) => {}
68+
>o4 : (o: keyof A["a"]) => void
69+
> : ^ ^^ ^^^^^^^^^
70+
>(o: keyof (A['a'])) => {} : (o: keyof A["a"]) => void
71+
> : ^ ^^ ^^^^^^^^^
72+
>o : "b" | "notNecessary"
73+
> : ^^^^^^^^^^^^^^^^^^^^
74+
75+
=== main.ts ===
76+
import * as d from './decl'
77+
>d : typeof d
78+
> : ^^^^^^^^
79+
80+
export const f = {...d}
81+
>f : { o1: (o: "value of b") => void; o2: (o: "value of a") => void; o3: (o: "value of a") => void; o4: (o: "b" | "notNecessary") => void; }
82+
> : ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
83+
>{...d} : { o1: (o: "value of b") => void; o2: (o: "value of a") => void; o3: (o: "value of a") => void; o4: (o: "b" | "notNecessary") => void; }
84+
> : ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
85+
>d : typeof d
86+
> : ^^^^^^^^
87+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @declaration: true
2+
// @target: esnext
3+
4+
// @filename: decl.ts
5+
const u = "X";
6+
type A = { a: { b : "value of b", notNecessary: typeof u }}
7+
const a = { a: "value of a", notNecessary: u } as const
8+
9+
10+
export const o1 = (o: A['a']['b']) => {}
11+
12+
export const o2 = (o: (typeof a)['a']) => {}
13+
export const o3 = (o: typeof a['a']) => {}
14+
15+
export const o4 = (o: keyof (A['a'])) => {}
16+
17+
// @filename: main.ts
18+
import * as d from './decl'
19+
20+
export const f = {...d}

0 commit comments

Comments
 (0)