Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better best effort type alias preservation #40232

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 177 additions & 66 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions src/compiler/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ namespace ts.tracing {
for (let i = 0; i < numTypes; i++) {
const type = types[i];
const objectFlags = (type as any).objectFlags;
const symbol = type.aliasSymbol ?? type.symbol;
const symbol = type.symbol;
const firstDeclaration = symbol?.declarations?.[0];
const firstFile = firstDeclaration && getSourceFileOfNode(firstDeclaration);

Expand Down Expand Up @@ -211,7 +211,6 @@ namespace ts.tracing {
recursionId: recursionToken,
unionTypes: (type.flags & TypeFlags.Union) ? (type as UnionType).types?.map(t => t.id) : undefined,
intersectionTypes: (type.flags & TypeFlags.Intersection) ? (type as IntersectionType).types.map(t => t.id) : undefined,
aliasTypeArguments: type.aliasTypeArguments?.map(t => t.id),
keyofType: (type.flags & TypeFlags.Index) ? (type as IndexType).type?.id : undefined,
...indexedAccessProperties,
...referenceProperties,
Expand Down
38 changes: 29 additions & 9 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4818,6 +4818,7 @@ namespace ts {
ClassWithConstructorReference = 0x01000000, // Class that contains a binding to its constructor inside of the class body.
ConstructorReferenceInClass = 0x02000000, // Binding to a class constructor inside of the class's body.
ContainsClassWithPrivateIdentifiers = 0x04000000, // Marked on all block-scoped containers containing a class with private identifiers.
AllTypeAliasesAcessibleAreMaterialized = 0x08000000, // Indicates that, if the file hasn't been typechecked, it's at least had its imports traversed and checked
}

/* @internal */
Expand Down Expand Up @@ -4935,6 +4936,8 @@ namespace ts {
IncludesWildcard = IndexedAccess,
/* @internal */
IncludesEmptyObject = Conditional,
/* @internal */
SupportsAliases = Union | Intersection | Object | IndexedAccess | Conditional, // This list is likely open to expansion
}

export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
Expand All @@ -4949,9 +4952,6 @@ namespace ts {
/* @internal */ checker: TypeChecker;
symbol: Symbol; // Symbol associated with type (if any)
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
aliasSymbol?: Symbol; // Alias associated with type
aliasTypeArguments?: readonly Type[]; // Alias type arguments (if any)
/* @internal */ aliasTypeArgumentsContainsMarker?: boolean; // Alias type arguments (if any)
/* @internal */
permissiveInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
/* @internal */
Expand Down Expand Up @@ -5066,6 +5066,8 @@ namespace ts {
RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral,
/* @internal */
PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType,
/* @internal */
SupportsAliases = Anonymous | Mapped | ReverseMapped | Reference,

// Object flags that uniquely identify the kind of ObjectType
/* @internal */
Expand Down Expand Up @@ -5344,8 +5346,6 @@ namespace ts {
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];
instantiations?: Map<Type>;
aliasSymbol?: Symbol;
aliasTypeArguments?: Type[];
}

// T extends U ? X : Y (TypeFlags.Conditional)
Expand Down Expand Up @@ -5475,10 +5475,10 @@ namespace ts {

/* @internal */
export type TypeMapper =
| { kind: TypeMapKind.Simple, source: Type, target: Type }
| { kind: TypeMapKind.Array, sources: readonly Type[], targets: readonly Type[] | undefined }
| { kind: TypeMapKind.Function, func: (t: Type) => Type }
| { kind: TypeMapKind.Composite | TypeMapKind.Merged, mapper1: TypeMapper, mapper2: TypeMapper };
| { kind: TypeMapKind.Simple, source: Type, target: Type, aliasCache?: Set<Type> }
| { kind: TypeMapKind.Array, sources: readonly Type[], targets: readonly Type[] | undefined, aliasCache?: Set<Type> }
| { kind: TypeMapKind.Function, func: (t: Type) => Type, aliasCache?: Set<Type> }
| { kind: TypeMapKind.Composite | TypeMapKind.Merged, mapper1: TypeMapper, mapper2: TypeMapper, aliasCache?: Set<Type> };

export const enum InferencePriority {
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
Expand Down Expand Up @@ -8153,4 +8153,24 @@ namespace ts {
negative: boolean;
base10Value: string;
}

export type TypeAlias = AliasReference | AliasKeyof;

export enum AliasKind {
Reference,
Keyof, // Unimplemented
}

export interface AliasReference {
kind: AliasKind.Reference;
symbol: Symbol;
typeArguments?: readonly Type[];
/* @internal */
typeArgumentsContainMarker?: boolean;
}

export interface AliasKeyof {
kind: AliasKind.Keyof;
type: Type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//// [tests/cases/compiler/aliasDeclaredAfterFirstUsageUsedByDeclarationEmit.ts] ////

//// [init.ts]
export interface A {
a: true;
}

export interface B {
b: true;
}

export function f(thing?: A | B): A | B { return null as any; };
//// [utils.ts]
import {A, B} from "./init";

export type Either = A | B;
//// [usage.ts]
import {Either} from "./utils";
import {f} from "./init";

export function doThing() {
return f();
}

//// [init.js]
"use strict";
exports.__esModule = true;
exports.f = void 0;
function f(thing) { return null; }
exports.f = f;
;
//// [utils.js]
"use strict";
exports.__esModule = true;
//// [usage.js]
"use strict";
exports.__esModule = true;
exports.doThing = void 0;
var init_1 = require("./init");
function doThing() {
return init_1.f();
}
exports.doThing = doThing;


//// [init.d.ts]
export interface A {
a: true;
}
export interface B {
b: true;
}
export declare function f(thing?: A | B): A | B;
//// [utils.d.ts]
import { A, B } from "./init";
export declare type Either = A | B;
//// [usage.d.ts]
import { Either } from "./utils";
export declare function doThing(): Either;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
=== tests/cases/compiler/init.ts ===
export interface A {
>A : Symbol(A, Decl(init.ts, 0, 0))

a: true;
>a : Symbol(A.a, Decl(init.ts, 0, 20))
}

export interface B {
>B : Symbol(B, Decl(init.ts, 2, 1))

b: true;
>b : Symbol(B.b, Decl(init.ts, 4, 20))
}

export function f(thing?: A | B): A | B { return null as any; };
>f : Symbol(f, Decl(init.ts, 6, 1))
>thing : Symbol(thing, Decl(init.ts, 8, 18))
>A : Symbol(A, Decl(init.ts, 0, 0))
>B : Symbol(B, Decl(init.ts, 2, 1))
>A : Symbol(A, Decl(init.ts, 0, 0))
>B : Symbol(B, Decl(init.ts, 2, 1))

=== tests/cases/compiler/utils.ts ===
import {A, B} from "./init";
>A : Symbol(A, Decl(utils.ts, 0, 8))
>B : Symbol(B, Decl(utils.ts, 0, 10))

export type Either = A | B;
>Either : Symbol(Either, Decl(utils.ts, 0, 28))
>A : Symbol(A, Decl(utils.ts, 0, 8))
>B : Symbol(B, Decl(utils.ts, 0, 10))

=== tests/cases/compiler/usage.ts ===
import {Either} from "./utils";
>Either : Symbol(Either, Decl(usage.ts, 0, 8))

import {f} from "./init";
>f : Symbol(f, Decl(usage.ts, 1, 8))

export function doThing() {
>doThing : Symbol(doThing, Decl(usage.ts, 1, 25))

return f();
>f : Symbol(f, Decl(usage.ts, 1, 8))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
=== tests/cases/compiler/init.ts ===
export interface A {
a: true;
>a : true
>true : true
}

export interface B {
b: true;
>b : true
>true : true
}

export function f(thing?: A | B): A | B { return null as any; };
>f : (thing?: A | B) => A | B
>thing : import("tests/cases/compiler/utils").Either
>null as any : any
>null : null

=== tests/cases/compiler/utils.ts ===
import {A, B} from "./init";
>A : any
>B : any

export type Either = A | B;
>Either : Either

=== tests/cases/compiler/usage.ts ===
import {Either} from "./utils";
>Either : any

import {f} from "./init";
>f : (thing?: Either) => Either

export function doThing() {
>doThing : () => Either

return f();
>f() : Either
>f : (thing?: Either) => Either
}
18 changes: 14 additions & 4 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2504,8 +2504,6 @@ declare namespace ts {
flags: TypeFlags;
symbol: Symbol;
pattern?: DestructuringPattern;
aliasSymbol?: Symbol;
aliasTypeArguments?: readonly Type[];
}
export interface LiteralType extends Type {
value: string | number | PseudoBigInt;
Expand Down Expand Up @@ -2638,8 +2636,6 @@ declare namespace ts {
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];
instantiations?: Map<Type>;
aliasSymbol?: Symbol;
aliasTypeArguments?: Type[];
}
export interface ConditionalType extends InstantiableType {
root: ConditionalRoot;
Expand Down Expand Up @@ -3874,6 +3870,20 @@ declare namespace ts {
negative: boolean;
base10Value: string;
}
export type TypeAlias = AliasReference | AliasKeyof;
export enum AliasKind {
Reference = 0,
Keyof = 1
}
export interface AliasReference {
kind: AliasKind.Reference;
symbol: Symbol;
typeArguments?: readonly Type[];
}
export interface AliasKeyof {
kind: AliasKind.Keyof;
type: Type;
}
export {};
}
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
Expand Down
18 changes: 14 additions & 4 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2504,8 +2504,6 @@ declare namespace ts {
flags: TypeFlags;
symbol: Symbol;
pattern?: DestructuringPattern;
aliasSymbol?: Symbol;
aliasTypeArguments?: readonly Type[];
}
export interface LiteralType extends Type {
value: string | number | PseudoBigInt;
Expand Down Expand Up @@ -2638,8 +2636,6 @@ declare namespace ts {
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];
instantiations?: Map<Type>;
aliasSymbol?: Symbol;
aliasTypeArguments?: Type[];
}
export interface ConditionalType extends InstantiableType {
root: ConditionalRoot;
Expand Down Expand Up @@ -3874,6 +3870,20 @@ declare namespace ts {
negative: boolean;
base10Value: string;
}
export type TypeAlias = AliasReference | AliasKeyof;
export enum AliasKind {
Reference = 0,
Keyof = 1
}
export interface AliasReference {
kind: AliasKind.Reference;
symbol: Symbol;
typeArguments?: readonly Type[];
}
export interface AliasKeyof {
kind: AliasKind.Keyof;
type: Type;
}
export {};
}
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(58,5): error TS2322: Type 'S' is not assignable to type 'T'.
Property 'c' is missing in type 'S' but required in type '{ a: 2; b: 4 | 3; c: string; }'.
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(82,5): error TS2322: Type 'S' is not assignable to type 'T'.
Type 'S' is not assignable to type '{ a: 0 | 2 | 1; b: 0 | 2 | 1; c: 2; }'.
Type 'S' is not assignable to type '{ a: N; b: N; c: 2; }'.
Types of property 'c' are incompatible.
Type '0 | 2 | 1' is not assignable to type '2'.
Type 'N' is not assignable to type '2'.
Type '0' is not assignable to type '2'.


Expand Down Expand Up @@ -107,9 +107,9 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
t = s;
~
!!! error TS2322: Type 'S' is not assignable to type 'T'.
!!! error TS2322: Type 'S' is not assignable to type '{ a: 0 | 2 | 1; b: 0 | 2 | 1; c: 2; }'.
!!! error TS2322: Type 'S' is not assignable to type '{ a: N; b: N; c: 2; }'.
!!! error TS2322: Types of property 'c' are incompatible.
!!! error TS2322: Type '0 | 2 | 1' is not assignable to type '2'.
!!! error TS2322: Type 'N' is not assignable to type '2'.
!!! error TS2322: Type '0' is not assignable to type '2'.
}

Expand Down
Loading