mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-24 02:21:30 -05:00
Resolve keyof and index operations instead of their targets. (#58758)
This commit is contained in:
committed by
GitHub
parent
145b106c20
commit
370228311c
@@ -1088,6 +1088,7 @@ import {
|
||||
UnionType,
|
||||
UnionTypeNode,
|
||||
UniqueESSymbolType,
|
||||
unwrapParenthesizedType,
|
||||
usingSingleLineStringWriter,
|
||||
VariableDeclaration,
|
||||
VariableDeclarationList,
|
||||
@@ -8601,7 +8602,58 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return enterNewScope(context, node, getParametersInScope(node), getTypeParametersInScope(node));
|
||||
}
|
||||
|
||||
function tryVisitTypeReference(node: TypeReferenceNode) {
|
||||
function tryVisitSimpleTypeNode(node: TypeNode): TypeNode | undefined {
|
||||
const innerNode = unwrapParenthesizedType(node);
|
||||
switch (innerNode.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
return tryVisitTypeReference(innerNode as TypeReferenceNode);
|
||||
case SyntaxKind.TypeQuery:
|
||||
return tryVisitTypeQuery(innerNode as TypeQueryNode);
|
||||
case SyntaxKind.IndexedAccessType:
|
||||
return tryVisitIndexedAccess(innerNode as IndexedAccessTypeNode);
|
||||
case SyntaxKind.TypeOperator:
|
||||
const typeOperatorNode = innerNode as TypeOperatorNode;
|
||||
if (typeOperatorNode.operator === SyntaxKind.KeyOfKeyword) {
|
||||
return tryVisitKeyOf(typeOperatorNode);
|
||||
}
|
||||
}
|
||||
return visitNode(node, visitExistingNodeTreeSymbols, isTypeNode);
|
||||
}
|
||||
|
||||
function tryVisitIndexedAccess(node: IndexedAccessTypeNode): TypeNode | undefined {
|
||||
const resultObjectType = tryVisitSimpleTypeNode(node.objectType);
|
||||
if (resultObjectType === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return factory.updateIndexedAccessTypeNode(node, resultObjectType, visitNode(node.indexType, visitExistingNodeTreeSymbols, isTypeNode)!);
|
||||
}
|
||||
|
||||
function tryVisitKeyOf(node: TypeOperatorNode): TypeNode | undefined {
|
||||
Debug.assertEqual(node.operator, SyntaxKind.KeyOfKeyword);
|
||||
const type = tryVisitSimpleTypeNode(node.type);
|
||||
if (type === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return factory.updateTypeOperatorNode(node, type);
|
||||
}
|
||||
|
||||
function tryVisitTypeQuery(node: TypeQueryNode): TypeNode | undefined {
|
||||
const { introducesError, node: exprName } = trackExistingEntityName(node.exprName, context);
|
||||
if (!introducesError) {
|
||||
return factory.updateTypeQueryNode(
|
||||
node,
|
||||
exprName,
|
||||
visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode),
|
||||
);
|
||||
}
|
||||
|
||||
const serializedName = serializeTypeName(context, node.exprName, /*isTypeOf*/ true);
|
||||
if (serializedName) {
|
||||
return setTextRange(context, serializedName, node.exprName);
|
||||
}
|
||||
}
|
||||
|
||||
function tryVisitTypeReference(node: TypeReferenceNode): TypeNode | undefined {
|
||||
if (canReuseTypeNode(context, node)) {
|
||||
const { introducesError, node: newName } = trackExistingEntityName(node.typeName, context);
|
||||
const typeArguments = visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode);
|
||||
@@ -8728,13 +8780,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
);
|
||||
}
|
||||
|
||||
if (isIndexedAccessTypeNode(node) && isTypeReferenceNode(node.objectType)) {
|
||||
const objectType = tryVisitTypeReference(node.objectType);
|
||||
if (!objectType) {
|
||||
if (isIndexedAccessTypeNode(node)) {
|
||||
const result = tryVisitIndexedAccess(node);
|
||||
if (!result) {
|
||||
hadError = true;
|
||||
return node;
|
||||
}
|
||||
return factory.updateIndexedAccessTypeNode(node, objectType, visitNode(node.indexType, visitExistingNodeTreeSymbols, isTypeNode)!);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (isTypeReferenceNode(node)) {
|
||||
@@ -8790,20 +8842,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return visited;
|
||||
}
|
||||
if (isTypeQueryNode(node)) {
|
||||
const { introducesError, node: exprName } = trackExistingEntityName(node.exprName, context);
|
||||
if (introducesError) {
|
||||
const serializedName = serializeTypeName(context, node.exprName, /*isTypeOf*/ true);
|
||||
if (serializedName) {
|
||||
return setTextRange(context, serializedName, node.exprName);
|
||||
}
|
||||
const result = tryVisitTypeQuery(node);
|
||||
if (!result) {
|
||||
hadError = true;
|
||||
return node;
|
||||
}
|
||||
return factory.updateTypeQueryNode(
|
||||
node,
|
||||
exprName,
|
||||
visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
if (isComputedPropertyName(node) && isEntityNameExpression(node.expression)) {
|
||||
const { node: result, introducesError } = trackExistingEntityName(node.expression, context);
|
||||
@@ -8877,14 +8921,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
else if (node.operator === SyntaxKind.KeyOfKeyword) {
|
||||
if (isTypeReferenceNode(node.type)) {
|
||||
const type = tryVisitTypeReference(node.type);
|
||||
if (!type) {
|
||||
hadError = true;
|
||||
return node;
|
||||
}
|
||||
return factory.updateTypeOperatorNode(node, type);
|
||||
const result = tryVisitKeyOf(node);
|
||||
if (!result) {
|
||||
hadError = true;
|
||||
return node;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11644,6 +11644,14 @@ export function unwrapParenthesizedExpression(o: Expression) {
|
||||
return o;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function unwrapParenthesizedType(o: TypeNode) {
|
||||
while (o.kind === SyntaxKind.ParenthesizedType) {
|
||||
o = (o as ParenthesizedTypeNode).type;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function hasInferredType(node: Node): node is HasInferredType {
|
||||
Debug.type<HasInferredType>(node);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//// [tests/cases/compiler/declarationEmitResolveTypesIfNotReusable.ts] ////
|
||||
|
||||
//// [decl.ts]
|
||||
const u = "X";
|
||||
type A = { a: { b : "value of b", notNecessary: typeof u }}
|
||||
const a = { a: "value of a", notNecessary: u } as const
|
||||
|
||||
|
||||
export const o1 = (o: A['a']['b']) => {}
|
||||
|
||||
export const o2 = (o: (typeof a)['a']) => {}
|
||||
export const o3 = (o: typeof a['a']) => {}
|
||||
|
||||
export const o4 = (o: keyof (A['a'])) => {}
|
||||
|
||||
//// [main.ts]
|
||||
import * as d from './decl'
|
||||
|
||||
export const f = {...d}
|
||||
|
||||
//// [decl.js]
|
||||
const u = "X";
|
||||
const a = { a: "value of a", notNecessary: u };
|
||||
export const o1 = (o) => { };
|
||||
export const o2 = (o) => { };
|
||||
export const o3 = (o) => { };
|
||||
export const o4 = (o) => { };
|
||||
//// [main.js]
|
||||
import * as d from './decl';
|
||||
export const f = { ...d };
|
||||
|
||||
|
||||
//// [decl.d.ts]
|
||||
declare const u = "X";
|
||||
type A = {
|
||||
a: {
|
||||
b: "value of b";
|
||||
notNecessary: typeof u;
|
||||
};
|
||||
};
|
||||
declare const a: {
|
||||
readonly a: "value of a";
|
||||
readonly notNecessary: "X";
|
||||
};
|
||||
export declare const o1: (o: A["a"]["b"]) => void;
|
||||
export declare const o2: (o: (typeof a)["a"]) => void;
|
||||
export declare const o3: (o: (typeof a)["a"]) => void;
|
||||
export declare const o4: (o: keyof A["a"]) => void;
|
||||
export {};
|
||||
//// [main.d.ts]
|
||||
export declare const f: {
|
||||
o1: (o: "value of b") => void;
|
||||
o2: (o: "value of a") => void;
|
||||
o3: (o: "value of a") => void;
|
||||
o4: (o: "b" | "notNecessary") => void;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
//// [tests/cases/compiler/declarationEmitResolveTypesIfNotReusable.ts] ////
|
||||
|
||||
=== decl.ts ===
|
||||
const u = "X";
|
||||
>u : Symbol(u, Decl(decl.ts, 0, 5))
|
||||
|
||||
type A = { a: { b : "value of b", notNecessary: typeof u }}
|
||||
>A : Symbol(A, Decl(decl.ts, 0, 14))
|
||||
>a : Symbol(a, Decl(decl.ts, 1, 10))
|
||||
>b : Symbol(b, Decl(decl.ts, 1, 15))
|
||||
>notNecessary : Symbol(notNecessary, Decl(decl.ts, 1, 33))
|
||||
>u : Symbol(u, Decl(decl.ts, 0, 5))
|
||||
|
||||
const a = { a: "value of a", notNecessary: u } as const
|
||||
>a : Symbol(a, Decl(decl.ts, 2, 5))
|
||||
>a : Symbol(a, Decl(decl.ts, 2, 11))
|
||||
>notNecessary : Symbol(notNecessary, Decl(decl.ts, 2, 28))
|
||||
>u : Symbol(u, Decl(decl.ts, 0, 5))
|
||||
>const : Symbol(const)
|
||||
|
||||
|
||||
export const o1 = (o: A['a']['b']) => {}
|
||||
>o1 : Symbol(o1, Decl(decl.ts, 5, 12))
|
||||
>o : Symbol(o, Decl(decl.ts, 5, 19))
|
||||
>A : Symbol(A, Decl(decl.ts, 0, 14))
|
||||
|
||||
export const o2 = (o: (typeof a)['a']) => {}
|
||||
>o2 : Symbol(o2, Decl(decl.ts, 7, 12))
|
||||
>o : Symbol(o, Decl(decl.ts, 7, 19))
|
||||
>a : Symbol(a, Decl(decl.ts, 2, 5))
|
||||
|
||||
export const o3 = (o: typeof a['a']) => {}
|
||||
>o3 : Symbol(o3, Decl(decl.ts, 8, 12))
|
||||
>o : Symbol(o, Decl(decl.ts, 8, 19))
|
||||
>a : Symbol(a, Decl(decl.ts, 2, 5))
|
||||
|
||||
export const o4 = (o: keyof (A['a'])) => {}
|
||||
>o4 : Symbol(o4, Decl(decl.ts, 10, 12))
|
||||
>o : Symbol(o, Decl(decl.ts, 10, 19))
|
||||
>A : Symbol(A, Decl(decl.ts, 0, 14))
|
||||
|
||||
=== main.ts ===
|
||||
import * as d from './decl'
|
||||
>d : Symbol(d, Decl(main.ts, 0, 6))
|
||||
|
||||
export const f = {...d}
|
||||
>f : Symbol(f, Decl(main.ts, 2, 12))
|
||||
>d : Symbol(d, Decl(main.ts, 0, 6))
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
//// [tests/cases/compiler/declarationEmitResolveTypesIfNotReusable.ts] ////
|
||||
|
||||
=== decl.ts ===
|
||||
const u = "X";
|
||||
>u : "X"
|
||||
> : ^^^
|
||||
>"X" : "X"
|
||||
> : ^^^
|
||||
|
||||
type A = { a: { b : "value of b", notNecessary: typeof u }}
|
||||
>A : A
|
||||
> : ^
|
||||
>a : { b: "value of b"; notNecessary: typeof u; }
|
||||
> : ^^^^^ ^^^^^^^^^^^^^^^^ ^^^
|
||||
>b : "value of b"
|
||||
> : ^^^^^^^^^^^^
|
||||
>notNecessary : "X"
|
||||
> : ^^^
|
||||
>u : "X"
|
||||
> : ^^^
|
||||
|
||||
const a = { a: "value of a", notNecessary: u } as const
|
||||
>a : { readonly a: "value of a"; readonly notNecessary: "X"; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
>{ a: "value of a", notNecessary: u } as const : { readonly a: "value of a"; readonly notNecessary: "X"; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
>{ a: "value of a", notNecessary: u } : { readonly a: "value of a"; readonly notNecessary: "X"; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
>a : "value of a"
|
||||
> : ^^^^^^^^^^^^
|
||||
>"value of a" : "value of a"
|
||||
> : ^^^^^^^^^^^^
|
||||
>notNecessary : "X"
|
||||
> : ^^^
|
||||
>u : "X"
|
||||
> : ^^^
|
||||
|
||||
|
||||
export const o1 = (o: A['a']['b']) => {}
|
||||
>o1 : (o: A["a"]["b"]) => void
|
||||
> : ^ ^^ ^^^^^^^^^
|
||||
>(o: A['a']['b']) => {} : (o: A["a"]["b"]) => void
|
||||
> : ^ ^^ ^^^^^^^^^
|
||||
>o : "value of b"
|
||||
> : ^^^^^^^^^^^^
|
||||
|
||||
export const o2 = (o: (typeof a)['a']) => {}
|
||||
>o2 : (o: (typeof a)["a"]) => void
|
||||
> : ^ ^^^ ^ ^^^^^^^^^
|
||||
>(o: (typeof a)['a']) => {} : (o: (typeof a)["a"]) => void
|
||||
> : ^ ^^^ ^ ^^^^^^^^^
|
||||
>o : "value of a"
|
||||
> : ^^^^^^^^^^^^
|
||||
>a : { readonly a: "value of a"; readonly notNecessary: "X"; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
export const o3 = (o: typeof a['a']) => {}
|
||||
>o3 : (o: (typeof a)["a"]) => void
|
||||
> : ^ ^^^ ^ ^^^^^^^^^
|
||||
>(o: typeof a['a']) => {} : (o: (typeof a)["a"]) => void
|
||||
> : ^ ^^^ ^ ^^^^^^^^^
|
||||
>o : "value of a"
|
||||
> : ^^^^^^^^^^^^
|
||||
>a : { readonly a: "value of a"; readonly notNecessary: "X"; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
export const o4 = (o: keyof (A['a'])) => {}
|
||||
>o4 : (o: keyof A["a"]) => void
|
||||
> : ^ ^^ ^^^^^^^^^
|
||||
>(o: keyof (A['a'])) => {} : (o: keyof A["a"]) => void
|
||||
> : ^ ^^ ^^^^^^^^^
|
||||
>o : "b" | "notNecessary"
|
||||
> : ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
=== main.ts ===
|
||||
import * as d from './decl'
|
||||
>d : typeof d
|
||||
> : ^^^^^^^^
|
||||
|
||||
export const f = {...d}
|
||||
>f : { o1: (o: "value of b") => void; o2: (o: "value of a") => void; o3: (o: "value of a") => void; o4: (o: "b" | "notNecessary") => void; }
|
||||
> : ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
>{...d} : { o1: (o: "value of b") => void; o2: (o: "value of a") => void; o3: (o: "value of a") => void; o4: (o: "b" | "notNecessary") => void; }
|
||||
> : ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
>d : typeof d
|
||||
> : ^^^^^^^^
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// @declaration: true
|
||||
// @target: esnext
|
||||
|
||||
// @filename: decl.ts
|
||||
const u = "X";
|
||||
type A = { a: { b : "value of b", notNecessary: typeof u }}
|
||||
const a = { a: "value of a", notNecessary: u } as const
|
||||
|
||||
|
||||
export const o1 = (o: A['a']['b']) => {}
|
||||
|
||||
export const o2 = (o: (typeof a)['a']) => {}
|
||||
export const o3 = (o: typeof a['a']) => {}
|
||||
|
||||
export const o4 = (o: keyof (A['a'])) => {}
|
||||
|
||||
// @filename: main.ts
|
||||
import * as d from './decl'
|
||||
|
||||
export const f = {...d}
|
||||
Reference in New Issue
Block a user