Skip to content

Commit de37c87

Browse files
authored
Optimize deferred type references (microsoft#36607)
* Improve reasoning about when to create deferred type references * Accept new baselines * Fix minor issues * Handle default type arguments case in isDeferredTypeReferenceNode
1 parent 0a16032 commit de37c87

File tree

4 files changed

+57
-17
lines changed

4 files changed

+57
-17
lines changed

Diff for: src/compiler/checker.ts

+45-5
Original file line numberDiff line numberDiff line change
@@ -11090,7 +11090,7 @@ namespace ts {
1109011090
return errorType;
1109111091
}
1109211092
}
11093-
if (node.kind === SyntaxKind.TypeReference && isAliasedType(node)) {
11093+
if (node.kind === SyntaxKind.TypeReference && isDeferredTypeReferenceNode(<TypeReferenceNode>node, length(node.typeArguments) !== typeParameters.length)) {
1109411094
return createDeferredTypeReference(<GenericType>type, <TypeReferenceNode>node, /*mapper*/ undefined);
1109511095
}
1109611096
// In a type reference, the outer type parameters of the referenced class or interface are automatically
@@ -11552,10 +11552,19 @@ namespace ts {
1155211552
return getTupleTypeOfArity(node.elementTypes.length, minLength, !!restElement, readonly, /*associatedNames*/ undefined);
1155311553
}
1155411554

11555+
// Return true if the given type reference node is directly aliased or if it needs to be deferred
11556+
// because it is possibly contained in a circular chain of eagerly resolved types.
11557+
function isDeferredTypeReferenceNode(node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode, hasDefaultTypeArguments?: boolean) {
11558+
return !!getAliasSymbolForTypeNode(node) || isResolvedByTypeAlias(node) && (
11559+
node.kind === SyntaxKind.ArrayType ? mayResolveTypeAlias(node.elementType) :
11560+
node.kind === SyntaxKind.TupleType ? some(node.elementTypes, mayResolveTypeAlias) :
11561+
hasDefaultTypeArguments || some(node.typeArguments, mayResolveTypeAlias));
11562+
}
11563+
1155511564
// Return true when the given node is transitively contained in type constructs that eagerly
1155611565
// resolve their constituent types. We include SyntaxKind.TypeReference because type arguments
1155711566
// of type aliases are eagerly resolved.
11558-
function isAliasedType(node: Node): boolean {
11567+
function isResolvedByTypeAlias(node: Node): boolean {
1155911568
const parent = node.parent;
1156011569
switch (parent.kind) {
1156111570
case SyntaxKind.ParenthesizedType:
@@ -11565,21 +11574,52 @@ namespace ts {
1156511574
case SyntaxKind.IndexedAccessType:
1156611575
case SyntaxKind.ConditionalType:
1156711576
case SyntaxKind.TypeOperator:
11568-
return isAliasedType(parent);
11577+
return isResolvedByTypeAlias(parent);
1156911578
case SyntaxKind.TypeAliasDeclaration:
1157011579
return true;
1157111580
}
1157211581
return false;
1157311582
}
1157411583

11584+
// Return true if resolving the given node (i.e. getTypeFromTypeNode) possibly causes resolution
11585+
// of a type alias.
11586+
function mayResolveTypeAlias(node: Node): boolean {
11587+
switch (node.kind) {
11588+
case SyntaxKind.TypeReference:
11589+
return isJSDocTypeReference(node) || !!(resolveTypeReferenceName((<TypeReferenceNode>node).typeName, SymbolFlags.Type).flags & SymbolFlags.TypeAlias);
11590+
case SyntaxKind.TypeQuery:
11591+
return true;
11592+
case SyntaxKind.TypeOperator:
11593+
return (<TypeOperatorNode>node).operator !== SyntaxKind.UniqueKeyword && mayResolveTypeAlias((<TypeOperatorNode>node).type);
11594+
case SyntaxKind.ParenthesizedType:
11595+
case SyntaxKind.OptionalType:
11596+
case SyntaxKind.JSDocOptionalType:
11597+
case SyntaxKind.JSDocNullableType:
11598+
case SyntaxKind.JSDocNonNullableType:
11599+
case SyntaxKind.JSDocTypeExpression:
11600+
return mayResolveTypeAlias((<ParenthesizedTypeNode | OptionalTypeNode | JSDocTypeReferencingNode>node).type);
11601+
case SyntaxKind.RestType:
11602+
return (<RestTypeNode>node).type.kind !== SyntaxKind.ArrayType || mayResolveTypeAlias((<ArrayTypeNode>(<RestTypeNode>node).type).elementType);
11603+
case SyntaxKind.UnionType:
11604+
case SyntaxKind.IntersectionType:
11605+
return some((<UnionOrIntersectionTypeNode>node).types, mayResolveTypeAlias);
11606+
case SyntaxKind.IndexedAccessType:
11607+
return mayResolveTypeAlias((<IndexedAccessTypeNode>node).objectType) || mayResolveTypeAlias((<IndexedAccessTypeNode>node).indexType);
11608+
case SyntaxKind.ConditionalType:
11609+
return mayResolveTypeAlias((<ConditionalTypeNode>node).checkType) || mayResolveTypeAlias((<ConditionalTypeNode>node).extendsType) ||
11610+
mayResolveTypeAlias((<ConditionalTypeNode>node).trueType) || mayResolveTypeAlias((<ConditionalTypeNode>node).falseType);
11611+
}
11612+
return false;
11613+
}
11614+
1157511615
function getTypeFromArrayOrTupleTypeNode(node: ArrayTypeNode | TupleTypeNode): Type {
1157611616
const links = getNodeLinks(node);
1157711617
if (!links.resolvedType) {
1157811618
const target = getArrayOrTupleTargetType(node);
1157911619
if (target === emptyGenericType) {
1158011620
links.resolvedType = emptyObjectType;
1158111621
}
11582-
else if (isAliasedType(node)) {
11622+
else if (isDeferredTypeReferenceNode(node)) {
1158311623
links.resolvedType = node.kind === SyntaxKind.TupleType && node.elementTypes.length === 0 ? target :
1158411624
createDeferredTypeReference(target, node, /*mapper*/ undefined);
1158511625
}
@@ -12902,7 +12942,7 @@ namespace ts {
1290212942
return links.resolvedType;
1290312943
}
1290412944

12905-
function getAliasSymbolForTypeNode(node: TypeNode) {
12945+
function getAliasSymbolForTypeNode(node: Node) {
1290612946
let host = node.parent;
1290712947
while (isParenthesizedTypeNode(host) || isTypeOperatorNode(host) && host.operator === SyntaxKind.ReadonlyKeyword) {
1290812948
host = host.parent;

Diff for: tests/baselines/reference/checkJsxChildrenProperty3.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ class FetchUser extends React.Component<IFetchUserProps, any> {
3131

3232
? this.props.children(this.state.result)
3333
>this.props.children(this.state.result) : JSX.Element
34-
>this.props.children : ((user: IUser) => JSX.Element) | (((user: IUser) => JSX.Element) & string) | (((user: IUser) => JSX.Element) & number) | (((user: IUser) => JSX.Element) & false) | (((user: IUser) => JSX.Element) & true) | (((user: IUser) => JSX.Element) & React.ReactElement<any>) | (((user: IUser) => JSX.Element) & (string | number | boolean | React.ReactElement<any> | any[])[])
34+
>this.props.children : ((user: IUser) => JSX.Element) | (((user: IUser) => JSX.Element) & string) | (((user: IUser) => JSX.Element) & number) | (((user: IUser) => JSX.Element) & false) | (((user: IUser) => JSX.Element) & true) | (((user: IUser) => JSX.Element) & React.ReactElement<any>) | (((user: IUser) => JSX.Element) & (string | number | boolean | any[] | React.ReactElement<any>)[])
3535
>this.props : IFetchUserProps & { children?: React.ReactNode; }
3636
>this : this
3737
>props : IFetchUserProps & { children?: React.ReactNode; }
38-
>children : ((user: IUser) => JSX.Element) | (((user: IUser) => JSX.Element) & string) | (((user: IUser) => JSX.Element) & number) | (((user: IUser) => JSX.Element) & false) | (((user: IUser) => JSX.Element) & true) | (((user: IUser) => JSX.Element) & React.ReactElement<any>) | (((user: IUser) => JSX.Element) & (string | number | boolean | React.ReactElement<any> | any[])[])
38+
>children : ((user: IUser) => JSX.Element) | (((user: IUser) => JSX.Element) & string) | (((user: IUser) => JSX.Element) & number) | (((user: IUser) => JSX.Element) & false) | (((user: IUser) => JSX.Element) & true) | (((user: IUser) => JSX.Element) & React.ReactElement<any>) | (((user: IUser) => JSX.Element) & (string | number | boolean | any[] | React.ReactElement<any>)[])
3939
>this.state.result : any
4040
>this.state : any
4141
>this : this

Diff for: tests/baselines/reference/checkJsxChildrenProperty4.errors.txt

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
tests/cases/conformance/jsx/file.tsx(24,28): error TS2551: Property 'NAme' does not exist on type 'IUser'. Did you mean 'Name'?
2-
tests/cases/conformance/jsx/file.tsx(36,15): error TS2322: Type '(user: IUser) => Element' is not assignable to type 'string | number | boolean | ReactElement<any> | any[]'.
3-
Type '(user: IUser) => Element' is missing the following properties from type 'any[]': push, pop, concat, join, and 15 more.
4-
tests/cases/conformance/jsx/file.tsx(39,15): error TS2322: Type '(user: IUser) => Element' is not assignable to type 'string | number | boolean | ReactElement<any> | any[]'.
5-
Type '(user: IUser) => Element' is missing the following properties from type 'any[]': push, pop, concat, join, and 15 more.
2+
tests/cases/conformance/jsx/file.tsx(36,15): error TS2322: Type '(user: IUser) => Element' is not assignable to type 'string | number | boolean | any[] | ReactElement<any>'.
3+
Type '(user: IUser) => Element' is missing the following properties from type 'ReactElement<any>': type, props
4+
tests/cases/conformance/jsx/file.tsx(39,15): error TS2322: Type '(user: IUser) => Element' is not assignable to type 'string | number | boolean | any[] | ReactElement<any>'.
5+
Type '(user: IUser) => Element' is missing the following properties from type 'ReactElement<any>': type, props
66

77

88
==== tests/cases/conformance/jsx/file.tsx (3 errors) ====
@@ -50,17 +50,17 @@ tests/cases/conformance/jsx/file.tsx(39,15): error TS2322: Type '(user: IUser) =
5050
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5151
) }
5252
~~~~~~~~~~~~~
53-
!!! error TS2322: Type '(user: IUser) => Element' is not assignable to type 'string | number | boolean | ReactElement<any> | any[]'.
54-
!!! error TS2322: Type '(user: IUser) => Element' is missing the following properties from type 'any[]': push, pop, concat, join, and 15 more.
53+
!!! error TS2322: Type '(user: IUser) => Element' is not assignable to type 'string | number | boolean | any[] | ReactElement<any>'.
54+
!!! error TS2322: Type '(user: IUser) => Element' is missing the following properties from type 'ReactElement<any>': type, props
5555
!!! related TS6212 tests/cases/conformance/jsx/file.tsx:36:15: Did you mean to call this expression?
5656
{ user => (
5757
~~~~~~~~~
5858
<h1>{ user.Name }</h1>
5959
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6060
) }
6161
~~~~~~~~~~~~~
62-
!!! error TS2322: Type '(user: IUser) => Element' is not assignable to type 'string | number | boolean | ReactElement<any> | any[]'.
63-
!!! error TS2322: Type '(user: IUser) => Element' is missing the following properties from type 'any[]': push, pop, concat, join, and 15 more.
62+
!!! error TS2322: Type '(user: IUser) => Element' is not assignable to type 'string | number | boolean | any[] | ReactElement<any>'.
63+
!!! error TS2322: Type '(user: IUser) => Element' is missing the following properties from type 'ReactElement<any>': type, props
6464
!!! related TS6212 tests/cases/conformance/jsx/file.tsx:39:15: Did you mean to call this expression?
6565
</FetchUser>
6666
);

Diff for: tests/baselines/reference/checkJsxChildrenProperty4.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ class FetchUser extends React.Component<IFetchUserProps, any> {
3131

3232
? this.props.children(this.state.result)
3333
>this.props.children(this.state.result) : JSX.Element
34-
>this.props.children : ((user: IUser) => JSX.Element) | (((user: IUser) => JSX.Element) & string) | (((user: IUser) => JSX.Element) & number) | (((user: IUser) => JSX.Element) & false) | (((user: IUser) => JSX.Element) & true) | (((user: IUser) => JSX.Element) & React.ReactElement<any>) | (((user: IUser) => JSX.Element) & (string | number | boolean | React.ReactElement<any> | any[])[])
34+
>this.props.children : ((user: IUser) => JSX.Element) | (((user: IUser) => JSX.Element) & string) | (((user: IUser) => JSX.Element) & number) | (((user: IUser) => JSX.Element) & false) | (((user: IUser) => JSX.Element) & true) | (((user: IUser) => JSX.Element) & React.ReactElement<any>) | (((user: IUser) => JSX.Element) & (string | number | boolean | any[] | React.ReactElement<any>)[])
3535
>this.props : IFetchUserProps & { children?: React.ReactNode; }
3636
>this : this
3737
>props : IFetchUserProps & { children?: React.ReactNode; }
38-
>children : ((user: IUser) => JSX.Element) | (((user: IUser) => JSX.Element) & string) | (((user: IUser) => JSX.Element) & number) | (((user: IUser) => JSX.Element) & false) | (((user: IUser) => JSX.Element) & true) | (((user: IUser) => JSX.Element) & React.ReactElement<any>) | (((user: IUser) => JSX.Element) & (string | number | boolean | React.ReactElement<any> | any[])[])
38+
>children : ((user: IUser) => JSX.Element) | (((user: IUser) => JSX.Element) & string) | (((user: IUser) => JSX.Element) & number) | (((user: IUser) => JSX.Element) & false) | (((user: IUser) => JSX.Element) & true) | (((user: IUser) => JSX.Element) & React.ReactElement<any>) | (((user: IUser) => JSX.Element) & (string | number | boolean | any[] | React.ReactElement<any>)[])
3939
>this.state.result : any
4040
>this.state : any
4141
>this : this

0 commit comments

Comments
 (0)