Skip to content

Do not materialize optional undefined properties inside actual types #28727

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

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
402 changes: 255 additions & 147 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

29 changes: 19 additions & 10 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3890,7 +3890,9 @@ namespace ts {
/* @internal */
EmptyObject = ContainsAnyFunctionType,
/* @internal */
ConstructionFlags = NonWideningType | Wildcard | EmptyObject,
AllFresh = EnumLiteral,
/* @internal */
ConstructionFlags = NonWideningType | Wildcard | EmptyObject | AllFresh,
// The following flag is used for different purposes by maybeTypeOfKind
/* @internal */
GenericMappedType = ContainsWideningType
Expand Down Expand Up @@ -3988,6 +3990,8 @@ namespace ts {
/* @internal */ constructSignatures?: ReadonlyArray<Signature>; // Construct signatures of type
/* @internal */ stringIndexInfo?: IndexInfo; // String indexing info
/* @internal */ numberIndexInfo?: IndexInfo; // Numeric indexing info
/* @internal */ regularType?: this;
/* @internal */ freshType?: this;
}

/** Class and interface types (ObjectFlags.Class and ObjectFlags.Interface). */
Expand Down Expand Up @@ -4071,9 +4075,22 @@ namespace ts {
couldContainTypeVariables: boolean;
}

/* @internal */
export const enum UnionFlags {
PrimitiveOnly = 1 << 0,
FreshObjectsOnly = 1 << 1,
WidenedFreshObjectsOnly = 1 << 2,
}

export interface UnionType extends UnionOrIntersectionType {
/* @internal */
primitiveTypesOnly: boolean;
unionFlags: UnionFlags;
/* @internal */
propertyNameCache?: UnderscoreEscapedMap<true>; // Cache of possible property names
/* @internal */
freshType?: this; // Pointer back to the original `FreshObjectsOnly` union for `WidenedFreshObjectsOnly` unions
/* @internal */
regularType?: this; // Pointer to the widened union for `FreshObjectsOnly` unions, if it has been made
}

export interface IntersectionType extends UnionOrIntersectionType {
Expand Down Expand Up @@ -4122,14 +4139,6 @@ namespace ts {
constructSignatures: ReadonlyArray<Signature>; // Construct signatures of type
}

/* @internal */
// Object literals are initially marked fresh. Freshness disappears following an assignment,
// before a type assertion, or when an object literal's type is widened. The regular
// version of a fresh type is identical except for the TypeFlags.FreshObjectLiteral flag.
export interface FreshObjectLiteralType extends ResolvedType {
regularType: ResolvedType; // Regular version of fresh type
}

// Just a place to cache element types of iterables and iterators
/* @internal */
export interface IterableOrIteratorType extends ObjectType, UnionType {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace ts {
export const externalHelpersModuleNameText = "tslib";

export const defaultMaximumTruncationLength = 160;
export const defaultHardCapTrunctionLength = 100000; // Trunction limit used even when `noErrorTruncation` is on to avoid OOM when printing huge types

export function getDeclarationOfKind<T extends Declaration>(symbol: Symbol, kind: T["kind"]): T | undefined {
const declarations = symbol.declarations;
Expand Down
3 changes: 2 additions & 1 deletion src/harness/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,8 @@ namespace Harness {
{ name: "symlink", type: "string" },
{ name: "link", type: "string" },
// Emitted js baseline will print full paths for every output file
{ name: "fullEmitPaths", type: "boolean" }
{ name: "fullEmitPaths", type: "boolean" },
{ name: "skipTypeAndSymbol", type: "boolean" }
];

let optionsIndex: ts.Map<ts.CommandLineOption>;
Expand Down
3 changes: 3 additions & 0 deletions src/testRunner/compilerRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ class CompilerTest {
}

public verifyTypesAndSymbols() {
if (this.harnessSettings.skipTypeAndSymbol) {
return;
}
if (this.fileName.indexOf("APISample") >= 0) {
return;
}
Expand Down
68,593 changes: 68,593 additions & 0 deletions tests/baselines/reference/complexArrayLiteral.js

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions tests/baselines/reference/conditionalTypeDoesntSpinForever.types
Original file line number Diff line number Diff line change
Expand Up @@ -449,22 +449,22 @@ export enum PubSubRecordIsStoredInRedisAsA {
>{} : {}

buildNameFieldConstructor(soFar),
>buildNameFieldConstructor(soFar) : { name?: undefined; } | { name: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { name: TYPE; }>; }
>buildNameFieldConstructor(soFar) : {} | { name: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { name: TYPE; }>; }
>buildNameFieldConstructor : <SO_FAR>(soFar: SO_FAR) => { name?: undefined; } | { name: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { name: TYPE; }>; }
>soFar : SO_FAR

buildIdentifierFieldConstructor(soFar),
>buildIdentifierFieldConstructor(soFar) : { identifier?: undefined; } | { identifier: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { identifier: TYPE; }>; }
>buildIdentifierFieldConstructor(soFar) : {} | { identifier: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { identifier: TYPE; }>; }
>buildIdentifierFieldConstructor : <SO_FAR>(soFar: SO_FAR) => { identifier?: undefined; } | { identifier: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { identifier: TYPE; }>; }
>soFar : SO_FAR

buildRecordFieldConstructor(soFar),
>buildRecordFieldConstructor(soFar) : { record?: undefined; } | { record: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { record: TYPE; }>; }
>buildRecordFieldConstructor(soFar) : {} | { record: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { record: TYPE; }>; }
>buildRecordFieldConstructor : <SO_FAR>(soFar: SO_FAR) => { record?: undefined; } | { record: <TYPE>(instance?: TYPE) => BuildPubSubRecordType<SO_FAR & { record: TYPE; }>; }
>soFar : SO_FAR

buildStoredAsConstructor(soFar),
>buildStoredAsConstructor(soFar) : { storedAsJsonEncodedRedisString?: undefined; storedAsRedisHash?: undefined; } | { storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString; }>; storedAsRedisHash: () => BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.redisHash; }>; }
>buildStoredAsConstructor(soFar) : {} | { storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString; }>; storedAsRedisHash: () => BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.redisHash; }>; }
>buildStoredAsConstructor : <SO_FAR>(soFar: SO_FAR) => { storedAsJsonEncodedRedisString?: undefined; storedAsRedisHash?: undefined; } | { storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString; }>; storedAsRedisHash: () => BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.redisHash; }>; }
>soFar : SO_FAR

Expand All @@ -474,7 +474,7 @@ export enum PubSubRecordIsStoredInRedisAsA {
>soFar : SO_FAR

buildType(soFar)
>buildType(soFar) : { type?: undefined; fields?: undefined; hasField?: undefined; } | { type: SO_FAR; fields: () => Set<keyof SO_FAR>; hasField: (fieldName: string | number | symbol) => boolean; }
>buildType(soFar) : {} | { type: SO_FAR; fields: () => Set<keyof SO_FAR>; hasField: (fieldName: string | number | symbol) => boolean; }
>buildType : <SO_FAR>(soFar: SO_FAR) => { type?: undefined; fields?: undefined; hasField?: undefined; } | { type: SO_FAR; fields: () => Set<keyof SO_FAR>; hasField: (fieldName: string | number | symbol) => boolean; }
>soFar : SO_FAR

Expand Down

This file was deleted.

10 changes: 5 additions & 5 deletions tests/baselines/reference/objectLiteralNormalization.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts
tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts(9,1): error TS2322: Type '{ c: true; }' is not assignable to type '{ a: number; b?: undefined; c?: undefined; } | { a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'.
Type '{ c: true; }' is missing the following properties from type '{ a: number; b: string; c: boolean; }': a, b
tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts(17,1): error TS2322: Type '{ a: string; b: number; }' is not assignable to type '{ a: number; b: number; } | { a: string; b?: undefined; } | { a?: undefined; b?: undefined; }'.
Type '{ a: string; b: number; }' is not assignable to type '{ a?: undefined; b?: undefined; }'.
Type '{ a: string; b: number; }' is not assignable to type '{ a: number; b: number; }'.
Types of property 'a' are incompatible.
Type 'string' is not assignable to type 'undefined'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts(18,1): error TS2322: Type '{ a: number; }' is not assignable to type '{ a: number; b: number; } | { a: string; b?: undefined; } | { a?: undefined; b?: undefined; }'.
Property 'b' is missing in type '{ a: number; }' but required in type '{ a: number; b: number; }'.

Expand All @@ -21,7 +21,7 @@ tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts
a1 = { a: 0, b: 0 }; // Error
~
!!! error TS2322: Type 'number' is not assignable to type 'string | undefined'.
!!! related TS6500 tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts:2:47: The expected type comes from property 'b' which is declared here on type '{ a: number; b?: undefined; c?: undefined; } | { a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'
!!! related TS6500 tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts:2:29: The expected type comes from property 'b' which is declared here on type '{ a: number; b?: undefined; c?: undefined; } | { a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'
a1 = { b: "y" }; // Error
~~
!!! error TS2322: Type '{ b: string; }' is not assignable to type '{ a: number; b?: undefined; c?: undefined; } | { a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'.
Expand All @@ -40,9 +40,9 @@ tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts
a2 = { a: "def", b: 20 }; // Error
~~
!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type '{ a: number; b: number; } | { a: string; b?: undefined; } | { a?: undefined; b?: undefined; }'.
!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type '{ a?: undefined; b?: undefined; }'.
!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type '{ a: number; b: number; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type 'string' is not assignable to type 'undefined'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
a2 = { a: 1 }; // Error
~~
!!! error TS2322: Type '{ a: number; }' is not assignable to type '{ a: number; b: number; } | { a: string; b?: undefined; } | { a?: undefined; b?: undefined; }'.
Expand Down
32 changes: 16 additions & 16 deletions tests/baselines/reference/objectLiteralNormalization.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ a1.a; // number
>a : Symbol(a, Decl(objectLiteralNormalization.ts, 1, 11), Decl(objectLiteralNormalization.ts, 1, 21), Decl(objectLiteralNormalization.ts, 1, 39))

a1.b; // string | undefined
>a1.b : Symbol(b, Decl(objectLiteralNormalization.ts, 1, 45), Decl(objectLiteralNormalization.ts, 1, 27), Decl(objectLiteralNormalization.ts, 1, 45))
>a1.b : Symbol(b, Decl(objectLiteralNormalization.ts, 1, 27), Decl(objectLiteralNormalization.ts, 1, 45))
>a1 : Symbol(a1, Decl(objectLiteralNormalization.ts, 1, 3))
>b : Symbol(b, Decl(objectLiteralNormalization.ts, 1, 45), Decl(objectLiteralNormalization.ts, 1, 27), Decl(objectLiteralNormalization.ts, 1, 45))
>b : Symbol(b, Decl(objectLiteralNormalization.ts, 1, 27), Decl(objectLiteralNormalization.ts, 1, 45))

a1.c; // boolean | undefined
>a1.c : Symbol(c, Decl(objectLiteralNormalization.ts, 1, 53), Decl(objectLiteralNormalization.ts, 1, 53))
>a1.c : Symbol(c, Decl(objectLiteralNormalization.ts, 1, 53))
>a1 : Symbol(a1, Decl(objectLiteralNormalization.ts, 1, 3))
>c : Symbol(c, Decl(objectLiteralNormalization.ts, 1, 53), Decl(objectLiteralNormalization.ts, 1, 53))
>c : Symbol(c, Decl(objectLiteralNormalization.ts, 1, 53))

a1 = { a: 1 };
>a1 : Symbol(a1, Decl(objectLiteralNormalization.ts, 1, 3))
Expand All @@ -48,14 +48,14 @@ let a2 = [{ a: 1, b: 2 }, { a: "abc" }, {}][0];
>a : Symbol(a, Decl(objectLiteralNormalization.ts, 10, 27))

a2.a; // string | number | undefined
>a2.a : Symbol(a, Decl(objectLiteralNormalization.ts, 10, 11), Decl(objectLiteralNormalization.ts, 10, 27), Decl(objectLiteralNormalization.ts, 10, 27))
>a2.a : Symbol(a, Decl(objectLiteralNormalization.ts, 10, 11), Decl(objectLiteralNormalization.ts, 10, 27))
>a2 : Symbol(a2, Decl(objectLiteralNormalization.ts, 10, 3))
>a : Symbol(a, Decl(objectLiteralNormalization.ts, 10, 11), Decl(objectLiteralNormalization.ts, 10, 27), Decl(objectLiteralNormalization.ts, 10, 27))
>a : Symbol(a, Decl(objectLiteralNormalization.ts, 10, 11), Decl(objectLiteralNormalization.ts, 10, 27))

a2.b; // number | undefined
>a2.b : Symbol(b, Decl(objectLiteralNormalization.ts, 10, 17), Decl(objectLiteralNormalization.ts, 1, 45))
>a2.b : Symbol(b, Decl(objectLiteralNormalization.ts, 10, 17))
>a2 : Symbol(a2, Decl(objectLiteralNormalization.ts, 10, 3))
>b : Symbol(b, Decl(objectLiteralNormalization.ts, 10, 17), Decl(objectLiteralNormalization.ts, 1, 45))
>b : Symbol(b, Decl(objectLiteralNormalization.ts, 10, 17))

a2 = { a: 10, b: 20 };
>a2 : Symbol(a2, Decl(objectLiteralNormalization.ts, 10, 3))
Expand Down Expand Up @@ -144,32 +144,32 @@ d1.pos;
>pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))

d1.pos.x;
>d1.pos.x : Symbol(x, Decl(objectLiteralNormalization.ts, 33, 29), Decl(objectLiteralNormalization.ts, 33, 29))
>d1.pos.x : Symbol(x, Decl(objectLiteralNormalization.ts, 33, 29))
>d1.pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))
>d1 : Symbol(d1, Decl(objectLiteralNormalization.ts, 33, 3))
>pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))
>x : Symbol(x, Decl(objectLiteralNormalization.ts, 33, 29), Decl(objectLiteralNormalization.ts, 33, 29))
>x : Symbol(x, Decl(objectLiteralNormalization.ts, 33, 29))

d1.pos.y;
>d1.pos.y : Symbol(y, Decl(objectLiteralNormalization.ts, 33, 35), Decl(objectLiteralNormalization.ts, 33, 35))
>d1.pos.y : Symbol(y, Decl(objectLiteralNormalization.ts, 33, 35))
>d1.pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))
>d1 : Symbol(d1, Decl(objectLiteralNormalization.ts, 33, 3))
>pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))
>y : Symbol(y, Decl(objectLiteralNormalization.ts, 33, 35), Decl(objectLiteralNormalization.ts, 33, 35))
>y : Symbol(y, Decl(objectLiteralNormalization.ts, 33, 35))

d1.pos.a;
>d1.pos.a : Symbol(a, Decl(objectLiteralNormalization.ts, 10, 27), Decl(objectLiteralNormalization.ts, 33, 73))
>d1.pos.a : Symbol(a, Decl(objectLiteralNormalization.ts, 33, 73))
>d1.pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))
>d1 : Symbol(d1, Decl(objectLiteralNormalization.ts, 33, 3))
>pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))
>a : Symbol(a, Decl(objectLiteralNormalization.ts, 10, 27), Decl(objectLiteralNormalization.ts, 33, 73))
>a : Symbol(a, Decl(objectLiteralNormalization.ts, 33, 73))

d1.pos.b;
>d1.pos.b : Symbol(b, Decl(objectLiteralNormalization.ts, 1, 45), Decl(objectLiteralNormalization.ts, 33, 86))
>d1.pos.b : Symbol(b, Decl(objectLiteralNormalization.ts, 33, 86))
>d1.pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))
>d1 : Symbol(d1, Decl(objectLiteralNormalization.ts, 33, 3))
>pos : Symbol(pos, Decl(objectLiteralNormalization.ts, 33, 22), Decl(objectLiteralNormalization.ts, 33, 58))
>b : Symbol(b, Decl(objectLiteralNormalization.ts, 1, 45), Decl(objectLiteralNormalization.ts, 33, 86))
>b : Symbol(b, Decl(objectLiteralNormalization.ts, 33, 86))

declare function f<T>(...items: T[]): T;
>f : Symbol(f, Decl(objectLiteralNormalization.ts, 39, 9))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ ExpandoExpr.m = function(n: number) {
}
var n = (ExpandoExpr.prop.x || 0) + ExpandoExpr.m(12) + ExpandoExpr(101).length
>n : Symbol(n, Decl(typeFromPropertyAssignment29.ts, 7, 3), Decl(typeFromPropertyAssignment29.ts, 17, 3), Decl(typeFromPropertyAssignment29.ts, 45, 3), Decl(typeFromPropertyAssignment29.ts, 63, 3), Decl(typeFromPropertyAssignment29.ts, 73, 3) ... and 1 more)
>ExpandoExpr.prop.x : Symbol(x, Decl(typeFromPropertyAssignment29.ts, 12, 20))
>ExpandoExpr.prop.x : Symbol(x)
>ExpandoExpr.prop : Symbol(ExpandoExpr.prop, Decl(typeFromPropertyAssignment29.ts, 11, 1), Decl(typeFromPropertyAssignment29.ts, 12, 27))
>ExpandoExpr : Symbol(ExpandoExpr, Decl(typeFromPropertyAssignment29.ts, 9, 5), Decl(typeFromPropertyAssignment29.ts, 11, 1))
>prop : Symbol(ExpandoExpr.prop, Decl(typeFromPropertyAssignment29.ts, 11, 1), Decl(typeFromPropertyAssignment29.ts, 12, 27))
>x : Symbol(x, Decl(typeFromPropertyAssignment29.ts, 12, 20))
>x : Symbol(x)
>ExpandoExpr.m : Symbol(ExpandoExpr.m, Decl(typeFromPropertyAssignment29.ts, 13, 28))
>ExpandoExpr : Symbol(ExpandoExpr, Decl(typeFromPropertyAssignment29.ts, 9, 5), Decl(typeFromPropertyAssignment29.ts, 11, 1))
>m : Symbol(ExpandoExpr.m, Decl(typeFromPropertyAssignment29.ts, 13, 28))
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/typeFromPropertyAssignment29.types
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ var n = (ExpandoExpr.prop.x || 0) + ExpandoExpr.m(12) + ExpandoExpr(101).length
>(ExpandoExpr.prop.x || 0) : 0
>ExpandoExpr.prop.x || 0 : 0
>ExpandoExpr.prop.x : undefined
>ExpandoExpr.prop : { y: string; x?: undefined; }
>ExpandoExpr.prop : { y: string; }
>ExpandoExpr : { (n: number): string; prop: { x: number; y?: undefined; } | { y: string; x?: undefined; }; m(n: number): number; }
>prop : { y: string; x?: undefined; }
>prop : { y: string; }
>x : undefined
>0 : 0
>ExpandoExpr.m(12) : number
Expand Down
Loading