From 7f3d3fb75cbd0e80e334825b01da8f0e9671d8e5 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Thu, 5 Dec 2024 15:07:00 +0100 Subject: [PATCH 01/27] first impl --- src/compiler/checker.ts | 7 +- tests/baselines/reference/000.js | 73 ++++++++++++++++++++ tests/baselines/reference/000.symbols | 79 ++++++++++++++++++++++ tests/baselines/reference/000.types | 95 +++++++++++++++++++++++++++ tests/cases/compiler/000.ts | 44 +++++++++++++ 5 files changed, 296 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/000.js create mode 100644 tests/baselines/reference/000.symbols create mode 100644 tests/baselines/reference/000.types create mode 100644 tests/cases/compiler/000.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2dbd59bf51ef4..204459490aff8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27285,10 +27285,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (type && type.flags & TypeFlags.Union) { const prop = getUnionOrIntersectionProperty(type as UnionType, name); if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { + const propType = getTypeOfSymbol(prop) // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty if ((prop as TransientSymbol).links.isDiscriminantProperty === undefined) { - (prop as TransientSymbol).links.isDiscriminantProperty = ((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant && - !isGenericType(getTypeOfSymbol(prop)); + (prop as TransientSymbol).links.isDiscriminantProperty = + ((((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) + || !!(((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) + && !isGenericType(propType); } return !!(prop as TransientSymbol).links.isDiscriminantProperty; } diff --git a/tests/baselines/reference/000.js b/tests/baselines/reference/000.js new file mode 100644 index 0000000000000..68955d7670af8 --- /dev/null +++ b/tests/baselines/reference/000.js @@ -0,0 +1,73 @@ +//// [tests/cases/compiler/000.ts] //// + +//// [000.ts] +interface State { + state: Type; +} + +interface UserName { + first: string; + last?: string; +} + +// Can't union narrow of string | object: + +const nameState1 = {} as unknown as { + value: string; + state: State; +} | { + value: UserName; + state: State; +} +// | { +// value: undefined; +// state: State; +// }; + +if (typeof nameState1.value === "string") { + const a: State = nameState1.state; + + // ^^^^^^^^^ + // Type 'State | State' does not satisfy the expected type 'State'. + // Type 'State' is not assignable to type 'State'. + // Type 'UserName' is not assignable to type 'string'.(1360) +} + +// But it works if I add undefined to the mix: + +// const nameState2 = {} as unknown as { +// value: undefined; +// state: State; +// } | { +// value: string; +// state: State; +// } | { +// value: UserName; +// state: State; +// }; + +//// [000.js] +// Can't union narrow of string | object: +var nameState1 = {}; +// | { +// value: undefined; +// state: State; +// }; +if (typeof nameState1.value === "string") { + var a = nameState1.state; + // ^^^^^^^^^ + // Type 'State | State' does not satisfy the expected type 'State'. + // Type 'State' is not assignable to type 'State'. + // Type 'UserName' is not assignable to type 'string'.(1360) +} +// But it works if I add undefined to the mix: +// const nameState2 = {} as unknown as { +// value: undefined; +// state: State; +// } | { +// value: string; +// state: State; +// } | { +// value: UserName; +// state: State; +// }; diff --git a/tests/baselines/reference/000.symbols b/tests/baselines/reference/000.symbols new file mode 100644 index 0000000000000..7142891120e03 --- /dev/null +++ b/tests/baselines/reference/000.symbols @@ -0,0 +1,79 @@ +//// [tests/cases/compiler/000.ts] //// + +=== 000.ts === +interface State { +>State : Symbol(State, Decl(000.ts, 0, 0)) +>Type : Symbol(Type, Decl(000.ts, 0, 16)) + + state: Type; +>state : Symbol(State.state, Decl(000.ts, 0, 23)) +>Type : Symbol(Type, Decl(000.ts, 0, 16)) +} + +interface UserName { +>UserName : Symbol(UserName, Decl(000.ts, 2, 1)) + + first: string; +>first : Symbol(UserName.first, Decl(000.ts, 4, 20)) + + last?: string; +>last : Symbol(UserName.last, Decl(000.ts, 5, 16)) +} + +// Can't union narrow of string | object: + +const nameState1 = {} as unknown as { +>nameState1 : Symbol(nameState1, Decl(000.ts, 11, 5)) + + value: string; +>value : Symbol(value, Decl(000.ts, 11, 37)) + + state: State; +>state : Symbol(state, Decl(000.ts, 12, 16)) +>State : Symbol(State, Decl(000.ts, 0, 0)) + +} | { + value: UserName; +>value : Symbol(value, Decl(000.ts, 14, 5)) +>UserName : Symbol(UserName, Decl(000.ts, 2, 1)) + + state: State; +>state : Symbol(state, Decl(000.ts, 15, 18)) +>State : Symbol(State, Decl(000.ts, 0, 0)) +>UserName : Symbol(UserName, Decl(000.ts, 2, 1)) +} +// | { +// value: undefined; +// state: State; +// }; + +if (typeof nameState1.value === "string") { +>nameState1.value : Symbol(value, Decl(000.ts, 11, 37), Decl(000.ts, 14, 5)) +>nameState1 : Symbol(nameState1, Decl(000.ts, 11, 5)) +>value : Symbol(value, Decl(000.ts, 11, 37), Decl(000.ts, 14, 5)) + + const a: State = nameState1.state; +>a : Symbol(a, Decl(000.ts, 24, 7)) +>State : Symbol(State, Decl(000.ts, 0, 0)) +>nameState1.state : Symbol(state, Decl(000.ts, 12, 16)) +>nameState1 : Symbol(nameState1, Decl(000.ts, 11, 5)) +>state : Symbol(state, Decl(000.ts, 12, 16)) + + // ^^^^^^^^^ + // Type 'State | State' does not satisfy the expected type 'State'. + // Type 'State' is not assignable to type 'State'. + // Type 'UserName' is not assignable to type 'string'.(1360) +} + +// But it works if I add undefined to the mix: + +// const nameState2 = {} as unknown as { +// value: undefined; +// state: State; +// } | { +// value: string; +// state: State; +// } | { +// value: UserName; +// state: State; +// }; diff --git a/tests/baselines/reference/000.types b/tests/baselines/reference/000.types new file mode 100644 index 0000000000000..4e39b9ef9926b --- /dev/null +++ b/tests/baselines/reference/000.types @@ -0,0 +1,95 @@ +//// [tests/cases/compiler/000.ts] //// + +=== 000.ts === +interface State { + state: Type; +>state : Type +> : ^^^^ +} + +interface UserName { + first: string; +>first : string +> : ^^^^^^ + + last?: string; +>last : string +> : ^^^^^^ +} + +// Can't union narrow of string | object: + +const nameState1 = {} as unknown as { +>nameState1 : { value: string; state: State; } | { value: UserName; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>{} as unknown as { value: string; state: State;} | { value: UserName; state: State;} : { value: string; state: State; } | { value: UserName; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>{} as unknown : unknown +> : ^^^^^^^ +>{} : {} +> : ^^ + + value: string; +>value : string +> : ^^^^^^ + + state: State; +>state : State +> : ^^^^^^^^^^^^^ + +} | { + value: UserName; +>value : UserName +> : ^^^^^^^^ + + state: State; +>state : State +> : ^^^^^^^^^^^^^^^ +} +// | { +// value: undefined; +// state: State; +// }; + +if (typeof nameState1.value === "string") { +>typeof nameState1.value === "string" : boolean +> : ^^^^^^^ +>typeof nameState1.value : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>nameState1.value : string | UserName +> : ^^^^^^^^^^^^^^^^^ +>nameState1 : { value: string; state: State; } | { value: UserName; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>value : string | UserName +> : ^^^^^^^^^^^^^^^^^ +>"string" : "string" +> : ^^^^^^^^ + + const a: State = nameState1.state; +>a : State +> : ^^^^^^^^^^^^^ +>nameState1.state : State +> : ^^^^^^^^^^^^^ +>nameState1 : { value: string; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^ +>state : State +> : ^^^^^^^^^^^^^ + + // ^^^^^^^^^ + // Type 'State | State' does not satisfy the expected type 'State'. + // Type 'State' is not assignable to type 'State'. + // Type 'UserName' is not assignable to type 'string'.(1360) +} + +// But it works if I add undefined to the mix: + +// const nameState2 = {} as unknown as { +// value: undefined; +// state: State; +// } | { +// value: string; +// state: State; +// } | { +// value: UserName; +// state: State; +// }; diff --git a/tests/cases/compiler/000.ts b/tests/cases/compiler/000.ts new file mode 100644 index 0000000000000..490688e29087e --- /dev/null +++ b/tests/cases/compiler/000.ts @@ -0,0 +1,44 @@ +interface State { + state: Type; +} + +interface UserName { + first: string; + last?: string; +} + +// Can't union narrow of string | object: + +const nameState1 = {} as unknown as { + value: string; + state: State; +} | { + value: UserName; + state: State; +} +// | { +// value: undefined; +// state: State; +// }; + +if (typeof nameState1.value === "string") { + const a: State = nameState1.state; + + // ^^^^^^^^^ + // Type 'State | State' does not satisfy the expected type 'State'. + // Type 'State' is not assignable to type 'State'. + // Type 'UserName' is not assignable to type 'string'.(1360) +} + +// But it works if I add undefined to the mix: + +// const nameState2 = {} as unknown as { +// value: undefined; +// state: State; +// } | { +// value: string; +// state: State; +// } | { +// value: UserName; +// state: State; +// }; \ No newline at end of file From 1bc0a5538a5aa3f7b3b501793ecb5dada1c7b0ad Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Thu, 5 Dec 2024 15:13:57 +0100 Subject: [PATCH 02/27] clean test --- tests/baselines/reference/000.js | 57 +++------------------- tests/baselines/reference/000.symbols | 54 ++++++--------------- tests/baselines/reference/000.types | 69 +++++++++------------------ tests/cases/compiler/000.ts | 31 ++---------- 4 files changed, 48 insertions(+), 163 deletions(-) diff --git a/tests/baselines/reference/000.js b/tests/baselines/reference/000.js index 68955d7670af8..0f31fecec4b43 100644 --- a/tests/baselines/reference/000.js +++ b/tests/baselines/reference/000.js @@ -10,64 +10,21 @@ interface UserName { last?: string; } -// Can't union narrow of string | object: -const nameState1 = {} as unknown as { +const nameState = {} as { value: string; state: State; } | { value: UserName; state: State; -} -// | { -// value: undefined; -// state: State; -// }; - -if (typeof nameState1.value === "string") { - const a: State = nameState1.state; - - // ^^^^^^^^^ - // Type 'State | State' does not satisfy the expected type 'State'. - // Type 'State' is not assignable to type 'State'. - // Type 'UserName' is not assignable to type 'string'.(1360) } -// But it works if I add undefined to the mix: - -// const nameState2 = {} as unknown as { -// value: undefined; -// state: State; -// } | { -// value: string; -// state: State; -// } | { -// value: UserName; -// state: State; -// }; +if (typeof nameState.value === "string") { + nameState.state satisfies State; +} //// [000.js] -// Can't union narrow of string | object: -var nameState1 = {}; -// | { -// value: undefined; -// state: State; -// }; -if (typeof nameState1.value === "string") { - var a = nameState1.state; - // ^^^^^^^^^ - // Type 'State | State' does not satisfy the expected type 'State'. - // Type 'State' is not assignable to type 'State'. - // Type 'UserName' is not assignable to type 'string'.(1360) +var nameState = {}; +if (typeof nameState.value === "string") { + nameState.state; } -// But it works if I add undefined to the mix: -// const nameState2 = {} as unknown as { -// value: undefined; -// state: State; -// } | { -// value: string; -// state: State; -// } | { -// value: UserName; -// state: State; -// }; diff --git a/tests/baselines/reference/000.symbols b/tests/baselines/reference/000.symbols index 7142891120e03..fde1c93225078 100644 --- a/tests/baselines/reference/000.symbols +++ b/tests/baselines/reference/000.symbols @@ -20,60 +20,36 @@ interface UserName { >last : Symbol(UserName.last, Decl(000.ts, 5, 16)) } -// Can't union narrow of string | object: -const nameState1 = {} as unknown as { ->nameState1 : Symbol(nameState1, Decl(000.ts, 11, 5)) +const nameState = {} as { +>nameState : Symbol(nameState, Decl(000.ts, 10, 5)) value: string; ->value : Symbol(value, Decl(000.ts, 11, 37)) +>value : Symbol(value, Decl(000.ts, 10, 25)) state: State; ->state : Symbol(state, Decl(000.ts, 12, 16)) +>state : Symbol(state, Decl(000.ts, 11, 16)) >State : Symbol(State, Decl(000.ts, 0, 0)) } | { value: UserName; ->value : Symbol(value, Decl(000.ts, 14, 5)) +>value : Symbol(value, Decl(000.ts, 13, 5)) >UserName : Symbol(UserName, Decl(000.ts, 2, 1)) state: State; ->state : Symbol(state, Decl(000.ts, 15, 18)) +>state : Symbol(state, Decl(000.ts, 14, 18)) >State : Symbol(State, Decl(000.ts, 0, 0)) >UserName : Symbol(UserName, Decl(000.ts, 2, 1)) -} -// | { -// value: undefined; -// state: State; -// }; +} -if (typeof nameState1.value === "string") { ->nameState1.value : Symbol(value, Decl(000.ts, 11, 37), Decl(000.ts, 14, 5)) ->nameState1 : Symbol(nameState1, Decl(000.ts, 11, 5)) ->value : Symbol(value, Decl(000.ts, 11, 37), Decl(000.ts, 14, 5)) +if (typeof nameState.value === "string") { +>nameState.value : Symbol(value, Decl(000.ts, 10, 25), Decl(000.ts, 13, 5)) +>nameState : Symbol(nameState, Decl(000.ts, 10, 5)) +>value : Symbol(value, Decl(000.ts, 10, 25), Decl(000.ts, 13, 5)) - const a: State = nameState1.state; ->a : Symbol(a, Decl(000.ts, 24, 7)) + nameState.state satisfies State; +>nameState.state : Symbol(state, Decl(000.ts, 11, 16)) +>nameState : Symbol(nameState, Decl(000.ts, 10, 5)) +>state : Symbol(state, Decl(000.ts, 11, 16)) >State : Symbol(State, Decl(000.ts, 0, 0)) ->nameState1.state : Symbol(state, Decl(000.ts, 12, 16)) ->nameState1 : Symbol(nameState1, Decl(000.ts, 11, 5)) ->state : Symbol(state, Decl(000.ts, 12, 16)) - - // ^^^^^^^^^ - // Type 'State | State' does not satisfy the expected type 'State'. - // Type 'State' is not assignable to type 'State'. - // Type 'UserName' is not assignable to type 'string'.(1360) } - -// But it works if I add undefined to the mix: - -// const nameState2 = {} as unknown as { -// value: undefined; -// state: State; -// } | { -// value: string; -// state: State; -// } | { -// value: UserName; -// state: State; -// }; diff --git a/tests/baselines/reference/000.types b/tests/baselines/reference/000.types index 4e39b9ef9926b..2d3429267bf85 100644 --- a/tests/baselines/reference/000.types +++ b/tests/baselines/reference/000.types @@ -17,15 +17,12 @@ interface UserName { > : ^^^^^^ } -// Can't union narrow of string | object: -const nameState1 = {} as unknown as { ->nameState1 : { value: string; state: State; } | { value: UserName; state: State; } -> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ ->{} as unknown as { value: string; state: State;} | { value: UserName; state: State;} : { value: string; state: State; } | { value: UserName; state: State; } -> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ ->{} as unknown : unknown -> : ^^^^^^^ +const nameState = {} as { +>nameState : { value: string; state: State; } | { value: UserName; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>{} as { value: string; state: State;} | { value: UserName; state: State;} : { value: string; state: State; } | { value: UserName; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ >{} : {} > : ^^ @@ -45,51 +42,29 @@ const nameState1 = {} as unknown as { state: State; >state : State > : ^^^^^^^^^^^^^^^ -} -// | { -// value: undefined; -// state: State; -// }; +} -if (typeof nameState1.value === "string") { ->typeof nameState1.value === "string" : boolean -> : ^^^^^^^ ->typeof nameState1.value : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->nameState1.value : string | UserName -> : ^^^^^^^^^^^^^^^^^ ->nameState1 : { value: string; state: State; } | { value: UserName; state: State; } -> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +if (typeof nameState.value === "string") { +>typeof nameState.value === "string" : boolean +> : ^^^^^^^ +>typeof nameState.value : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>nameState.value : string | UserName +> : ^^^^^^^^^^^^^^^^^ +>nameState : { value: string; state: State; } | { value: UserName; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ >value : string | UserName > : ^^^^^^^^^^^^^^^^^ >"string" : "string" > : ^^^^^^^^ - const a: State = nameState1.state; ->a : State -> : ^^^^^^^^^^^^^ ->nameState1.state : State -> : ^^^^^^^^^^^^^ ->nameState1 : { value: string; state: State; } -> : ^^^^^^^^^ ^^^^^^^^^ ^^^ + nameState.state satisfies State; +>nameState.state satisfies State : State +> : ^^^^^^^^^^^^^ +>nameState.state : State +> : ^^^^^^^^^^^^^ +>nameState : { value: string; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^ >state : State > : ^^^^^^^^^^^^^ - - // ^^^^^^^^^ - // Type 'State | State' does not satisfy the expected type 'State'. - // Type 'State' is not assignable to type 'State'. - // Type 'UserName' is not assignable to type 'string'.(1360) } - -// But it works if I add undefined to the mix: - -// const nameState2 = {} as unknown as { -// value: undefined; -// state: State; -// } | { -// value: string; -// state: State; -// } | { -// value: UserName; -// state: State; -// }; diff --git a/tests/cases/compiler/000.ts b/tests/cases/compiler/000.ts index 490688e29087e..67a9d4b3de5d3 100644 --- a/tests/cases/compiler/000.ts +++ b/tests/cases/compiler/000.ts @@ -7,38 +7,15 @@ interface UserName { last?: string; } -// Can't union narrow of string | object: -const nameState1 = {} as unknown as { +const nameState = {} as { value: string; state: State; } | { value: UserName; state: State; -} -// | { -// value: undefined; -// state: State; -// }; - -if (typeof nameState1.value === "string") { - const a: State = nameState1.state; - - // ^^^^^^^^^ - // Type 'State | State' does not satisfy the expected type 'State'. - // Type 'State' is not assignable to type 'State'. - // Type 'UserName' is not assignable to type 'string'.(1360) } -// But it works if I add undefined to the mix: - -// const nameState2 = {} as unknown as { -// value: undefined; -// state: State; -// } | { -// value: string; -// state: State; -// } | { -// value: UserName; -// state: State; -// }; \ No newline at end of file +if (typeof nameState.value === "string") { + nameState.state satisfies State; +} \ No newline at end of file From 48e86a7fd2b98de89121bbf971e9204c56ba8e05 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Thu, 5 Dec 2024 21:48:39 +0100 Subject: [PATCH 03/27] second attempt --- src/compiler/checker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 204459490aff8..87ffe8edc8234 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27281,7 +27281,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } - function isDiscriminantProperty(type: Type | undefined, name: __String) { + function isDiscriminantProperty(type: Type | undefined, name: __String, skipNonUniformPrimitiveFallback: boolean = false) { if (type && type.flags & TypeFlags.Union) { const prop = getUnionOrIntersectionProperty(type as UnionType, name); if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { @@ -27290,7 +27290,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if ((prop as TransientSymbol).links.isDiscriminantProperty === undefined) { (prop as TransientSymbol).links.isDiscriminantProperty = ((((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) - || !!(((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) + || !!(((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && !skipNonUniformPrimitiveFallback && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) && !isGenericType(propType); } return !!(prop as TransientSymbol).links.isDiscriminantProperty; @@ -27302,7 +27302,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined { let result: Symbol[] | undefined; for (const sourceProperty of sourceProperties) { - if (isDiscriminantProperty(target, sourceProperty.escapedName)) { + if (isDiscriminantProperty(target, sourceProperty.escapedName, /*skipNonUniformPrimitiveFallback*/ true)) { if (result) { result.push(sourceProperty); continue; @@ -31982,10 +31982,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } if (p.kind === SyntaxKind.PropertyAssignment) { - return isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName); + return isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName, /*skipNonUniformPrimitiveFallback*/ true); } if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { - return isDiscriminantProperty(contextualType, p.symbol.escapedName); + return isDiscriminantProperty(contextualType, p.symbol.escapedName, /*skipNonUniformPrimitiveFallback*/ true); } return false; }), From dc0866ffeae1cb8eb6eff3739831ae7c51c6d968 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Thu, 5 Dec 2024 21:53:21 +0100 Subject: [PATCH 04/27] test --- tests/baselines/reference/000.js | 14 ++++++++++++- tests/baselines/reference/000.symbols | 15 ++++++++++++++ tests/baselines/reference/000.types | 30 +++++++++++++++++++++++++++ tests/cases/compiler/000.ts | 9 +++++++- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/000.js b/tests/baselines/reference/000.js index 0f31fecec4b43..38dbcbfb478d3 100644 --- a/tests/baselines/reference/000.js +++ b/tests/baselines/reference/000.js @@ -21,10 +21,22 @@ const nameState = {} as { if (typeof nameState.value === "string") { nameState.state satisfies State; -} +} + +declare const x: [string, number] | [number, string]; +if(x[0] === "string") { + x[1] satisfies number; +} // else { +// x[1] satisfies string; +// } //// [000.js] var nameState = {}; if (typeof nameState.value === "string") { nameState.state; } +if (x[0] === "string") { + x[1]; +} // else { +// x[1] satisfies string; +// } diff --git a/tests/baselines/reference/000.symbols b/tests/baselines/reference/000.symbols index fde1c93225078..019b01507d98e 100644 --- a/tests/baselines/reference/000.symbols +++ b/tests/baselines/reference/000.symbols @@ -53,3 +53,18 @@ if (typeof nameState.value === "string") { >state : Symbol(state, Decl(000.ts, 11, 16)) >State : Symbol(State, Decl(000.ts, 0, 0)) } + +declare const x: [string, number] | [number, string]; +>x : Symbol(x, Decl(000.ts, 22, 13)) + +if(x[0] === "string") { +>x : Symbol(x, Decl(000.ts, 22, 13)) +>0 : Symbol(0) + + x[1] satisfies number; +>x : Symbol(x, Decl(000.ts, 22, 13)) +>1 : Symbol(1) + +} // else { +// x[1] satisfies string; +// } diff --git a/tests/baselines/reference/000.types b/tests/baselines/reference/000.types index 2d3429267bf85..cac7b607bdc55 100644 --- a/tests/baselines/reference/000.types +++ b/tests/baselines/reference/000.types @@ -68,3 +68,33 @@ if (typeof nameState.value === "string") { >state : State > : ^^^^^^^^^^^^^ } + +declare const x: [string, number] | [number, string]; +>x : [string, number] | [number, string] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +if(x[0] === "string") { +>x[0] === "string" : boolean +> : ^^^^^^^ +>x[0] : string | number +> : ^^^^^^^^^^^^^^^ +>x : [string, number] | [number, string] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>0 : 0 +> : ^ +>"string" : "string" +> : ^^^^^^^^ + + x[1] satisfies number; +>x[1] satisfies number : number +> : ^^^^^^ +>x[1] : number +> : ^^^^^^ +>x : [string, number] +> : ^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + +} // else { +// x[1] satisfies string; +// } diff --git a/tests/cases/compiler/000.ts b/tests/cases/compiler/000.ts index 67a9d4b3de5d3..6eb4cd38353a4 100644 --- a/tests/cases/compiler/000.ts +++ b/tests/cases/compiler/000.ts @@ -18,4 +18,11 @@ const nameState = {} as { if (typeof nameState.value === "string") { nameState.state satisfies State; -} \ No newline at end of file +} + +declare const x: [string, number] | [number, string]; +if(x[0] === "string") { + x[1] satisfies number; +} // else { +// x[1] satisfies string; +// } \ No newline at end of file From 8542a992e6faacc90f4a141abe88648cb39d4f8c Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Thu, 5 Dec 2024 21:56:06 +0100 Subject: [PATCH 05/27] baselines --- tests/baselines/reference/000.js | 5 +++++ tests/baselines/reference/000.symbols | 14 +++++++++++--- tests/baselines/reference/000.types | 11 +++++++++++ tests/cases/compiler/000.ts | 2 ++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/000.js b/tests/baselines/reference/000.js index 38dbcbfb478d3..bbc1439c96deb 100644 --- a/tests/baselines/reference/000.js +++ b/tests/baselines/reference/000.js @@ -21,6 +21,8 @@ const nameState = {} as { if (typeof nameState.value === "string") { nameState.state satisfies State; +} else { + nameState.state satisfies State; } declare const x: [string, number] | [number, string]; @@ -35,6 +37,9 @@ var nameState = {}; if (typeof nameState.value === "string") { nameState.state; } +else { + nameState.state; +} if (x[0] === "string") { x[1]; } // else { diff --git a/tests/baselines/reference/000.symbols b/tests/baselines/reference/000.symbols index 019b01507d98e..c446895583c37 100644 --- a/tests/baselines/reference/000.symbols +++ b/tests/baselines/reference/000.symbols @@ -52,17 +52,25 @@ if (typeof nameState.value === "string") { >nameState : Symbol(nameState, Decl(000.ts, 10, 5)) >state : Symbol(state, Decl(000.ts, 11, 16)) >State : Symbol(State, Decl(000.ts, 0, 0)) + +} else { + nameState.state satisfies State; +>nameState.state : Symbol(state, Decl(000.ts, 14, 18)) +>nameState : Symbol(nameState, Decl(000.ts, 10, 5)) +>state : Symbol(state, Decl(000.ts, 14, 18)) +>State : Symbol(State, Decl(000.ts, 0, 0)) +>UserName : Symbol(UserName, Decl(000.ts, 2, 1)) } declare const x: [string, number] | [number, string]; ->x : Symbol(x, Decl(000.ts, 22, 13)) +>x : Symbol(x, Decl(000.ts, 24, 13)) if(x[0] === "string") { ->x : Symbol(x, Decl(000.ts, 22, 13)) +>x : Symbol(x, Decl(000.ts, 24, 13)) >0 : Symbol(0) x[1] satisfies number; ->x : Symbol(x, Decl(000.ts, 22, 13)) +>x : Symbol(x, Decl(000.ts, 24, 13)) >1 : Symbol(1) } // else { diff --git a/tests/baselines/reference/000.types b/tests/baselines/reference/000.types index cac7b607bdc55..52df635854497 100644 --- a/tests/baselines/reference/000.types +++ b/tests/baselines/reference/000.types @@ -67,6 +67,17 @@ if (typeof nameState.value === "string") { > : ^^^^^^^^^ ^^^^^^^^^ ^^^ >state : State > : ^^^^^^^^^^^^^ + +} else { + nameState.state satisfies State; +>nameState.state satisfies State : State +> : ^^^^^^^^^^^^^^^ +>nameState.state : State +> : ^^^^^^^^^^^^^^^ +>nameState : { value: UserName; state: State; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^ +>state : State +> : ^^^^^^^^^^^^^^^ } declare const x: [string, number] | [number, string]; diff --git a/tests/cases/compiler/000.ts b/tests/cases/compiler/000.ts index 6eb4cd38353a4..524090b2d1a87 100644 --- a/tests/cases/compiler/000.ts +++ b/tests/cases/compiler/000.ts @@ -18,6 +18,8 @@ const nameState = {} as { if (typeof nameState.value === "string") { nameState.state satisfies State; +} else { + nameState.state satisfies State; } declare const x: [string, number] | [number, string]; From 2b69230bf4f3592eff538988e71f2c9a31605eb3 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 11:34:11 +0100 Subject: [PATCH 06/27] this is even worse --- src/compiler/checker.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 87ffe8edc8234..3c0156a5013d1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27288,12 +27288,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const propType = getTypeOfSymbol(prop) // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty if ((prop as TransientSymbol).links.isDiscriminantProperty === undefined) { - (prop as TransientSymbol).links.isDiscriminantProperty = + (prop as TransientSymbol).links.isDiscriminantProperty = { + 'true': undefined, + 'false': undefined + } + } + + const key = skipNonUniformPrimitiveFallback ? "true" : "false" + + if((prop as TransientSymbol).links.isDiscriminantProperty![key] === undefined) { + (prop as TransientSymbol).links.isDiscriminantProperty![key] = ((((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) || !!(((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && !skipNonUniformPrimitiveFallback && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) && !isGenericType(propType); } - return !!(prop as TransientSymbol).links.isDiscriminantProperty; + return !!(prop as TransientSymbol).links.isDiscriminantProperty![key]; } } return false; From 6959a73cd2b9eeee954a365b03e0599e5ff996b2 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 11:34:23 +0100 Subject: [PATCH 07/27] and this --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 46ff57009e7fa..cf9ea75b06cbe 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6020,7 +6020,7 @@ export interface SymbolLinks { leftSpread?: Symbol; // Left source for synthetic spread property rightSpread?: Symbol; // Right source for synthetic spread property syntheticOrigin?: Symbol; // For a property on a mapped or spread type, points back to the original property - isDiscriminantProperty?: boolean; // True if discriminant synthetic property + isDiscriminantProperty?: { "true": boolean | undefined, "false": boolean | undefined }; // True if discriminant synthetic property resolvedExports?: SymbolTable; // Resolved exports of module or combined early- and late-bound static members of a class. resolvedMembers?: SymbolTable; // Combined early- and late-bound members of a symbol exportsChecked?: boolean; // True if exports of external module have been checked From a4c22c2e61e2b649b005c6c1e968c847279e7a9b Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 11:35:46 +0100 Subject: [PATCH 08/27] baselines --- tests/baselines/reference/000.js | 18 ++++++++++-------- tests/baselines/reference/000.symbols | 11 +++++++---- tests/baselines/reference/000.types | 23 +++++++++++++++++------ tests/cases/compiler/000.ts | 8 ++++---- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/tests/baselines/reference/000.js b/tests/baselines/reference/000.js index bbc1439c96deb..f3048c0ac0398 100644 --- a/tests/baselines/reference/000.js +++ b/tests/baselines/reference/000.js @@ -26,11 +26,12 @@ if (typeof nameState.value === "string") { } declare const x: [string, number] | [number, string]; -if(x[0] === "string") { +if (typeof x[0] === "string") { x[1] satisfies number; -} // else { -// x[1] satisfies string; -// } +} else { + x[1] satisfies string; +} + //// [000.js] var nameState = {}; @@ -40,8 +41,9 @@ if (typeof nameState.value === "string") { else { nameState.state; } -if (x[0] === "string") { +if (typeof x[0] === "string") { x[1]; -} // else { -// x[1] satisfies string; -// } +} +else { + x[1]; +} diff --git a/tests/baselines/reference/000.symbols b/tests/baselines/reference/000.symbols index c446895583c37..73593f57591f5 100644 --- a/tests/baselines/reference/000.symbols +++ b/tests/baselines/reference/000.symbols @@ -65,7 +65,7 @@ if (typeof nameState.value === "string") { declare const x: [string, number] | [number, string]; >x : Symbol(x, Decl(000.ts, 24, 13)) -if(x[0] === "string") { +if (typeof x[0] === "string") { >x : Symbol(x, Decl(000.ts, 24, 13)) >0 : Symbol(0) @@ -73,6 +73,9 @@ if(x[0] === "string") { >x : Symbol(x, Decl(000.ts, 24, 13)) >1 : Symbol(1) -} // else { -// x[1] satisfies string; -// } +} else { + x[1] satisfies string; +>x : Symbol(x, Decl(000.ts, 24, 13)) +>1 : Symbol(1) +} + diff --git a/tests/baselines/reference/000.types b/tests/baselines/reference/000.types index 52df635854497..86e58a694ded5 100644 --- a/tests/baselines/reference/000.types +++ b/tests/baselines/reference/000.types @@ -84,9 +84,11 @@ declare const x: [string, number] | [number, string]; >x : [string, number] | [number, string] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -if(x[0] === "string") { ->x[0] === "string" : boolean -> : ^^^^^^^ +if (typeof x[0] === "string") { +>typeof x[0] === "string" : boolean +> : ^^^^^^^ +>typeof x[0] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >x[0] : string | number > : ^^^^^^^^^^^^^^^ >x : [string, number] | [number, string] @@ -106,6 +108,15 @@ if(x[0] === "string") { >1 : 1 > : ^ -} // else { -// x[1] satisfies string; -// } +} else { + x[1] satisfies string; +>x[1] satisfies string : string +> : ^^^^^^ +>x[1] : string +> : ^^^^^^ +>x : [number, string] +> : ^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ +} + diff --git a/tests/cases/compiler/000.ts b/tests/cases/compiler/000.ts index 524090b2d1a87..bbf0b2409f72c 100644 --- a/tests/cases/compiler/000.ts +++ b/tests/cases/compiler/000.ts @@ -23,8 +23,8 @@ if (typeof nameState.value === "string") { } declare const x: [string, number] | [number, string]; -if(x[0] === "string") { +if (typeof x[0] === "string") { x[1] satisfies number; -} // else { -// x[1] satisfies string; -// } \ No newline at end of file +} else { + x[1] satisfies string; +} From fd99a9122f1dd24d2d6fe87cc3a2e3fcfcbdde63 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 11:43:55 +0100 Subject: [PATCH 09/27] the only baselines it's ok to change --- tests/baselines/reference/narrowingDestructuring.types | 4 ++-- tests/baselines/reference/narrowingTypeofDiscriminant.symbols | 4 ++-- tests/baselines/reference/narrowingTypeofDiscriminant.types | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/baselines/reference/narrowingDestructuring.types b/tests/baselines/reference/narrowingDestructuring.types index 0041dd06ab6d5..e1b37d376aa08 100644 --- a/tests/baselines/reference/narrowingDestructuring.types +++ b/tests/baselines/reference/narrowingDestructuring.types @@ -240,8 +240,8 @@ function farr(x: > : ^^^^^^ >tail : [string, string] | [number, number] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->x : [number, string, string] | [string, number, number] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>x : [number, string, string] +> : ^^^^^^^^^^^^^^^^^^^^^^^^ } } diff --git a/tests/baselines/reference/narrowingTypeofDiscriminant.symbols b/tests/baselines/reference/narrowingTypeofDiscriminant.symbols index b1ad133b80425..84f48c655ffda 100644 --- a/tests/baselines/reference/narrowingTypeofDiscriminant.symbols +++ b/tests/baselines/reference/narrowingTypeofDiscriminant.symbols @@ -67,9 +67,9 @@ function numberOk(wrapped: WrappedStringOr | null) { return null; } return wrapped.value; ->wrapped.value : Symbol(value, Decl(narrowingTypeofDiscriminant.ts, 20, 27), Decl(narrowingTypeofDiscriminant.ts, 20, 48)) +>wrapped.value : Symbol(value, Decl(narrowingTypeofDiscriminant.ts, 20, 27)) >wrapped : Symbol(wrapped, Decl(narrowingTypeofDiscriminant.ts, 22, 18)) ->value : Symbol(value, Decl(narrowingTypeofDiscriminant.ts, 20, 27), Decl(narrowingTypeofDiscriminant.ts, 20, 48)) +>value : Symbol(value, Decl(narrowingTypeofDiscriminant.ts, 20, 27)) } function booleanBad(wrapped: WrappedStringOr | null) { diff --git a/tests/baselines/reference/narrowingTypeofDiscriminant.types b/tests/baselines/reference/narrowingTypeofDiscriminant.types index 33e2111a90856..e0510721b2bd2 100644 --- a/tests/baselines/reference/narrowingTypeofDiscriminant.types +++ b/tests/baselines/reference/narrowingTypeofDiscriminant.types @@ -114,8 +114,8 @@ function numberOk(wrapped: WrappedStringOr | null) { return wrapped.value; >wrapped.value : string > : ^^^^^^ ->wrapped : WrappedStringOr -> : ^^^^^^^^^^^^^^^^^^^^^^^ +>wrapped : { value?: string; } +> : ^^^^^^^^^^ ^^^ >value : string > : ^^^^^^ } From 6f6d928774f67335744ad266d2c418b90c7a04bc Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 12:13:39 +0100 Subject: [PATCH 10/27] no flag needed here --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c0156a5013d1..2afaad2c00e8b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31991,10 +31991,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } if (p.kind === SyntaxKind.PropertyAssignment) { - return isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName, /*skipNonUniformPrimitiveFallback*/ true); + return isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName); } if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { - return isDiscriminantProperty(contextualType, p.symbol.escapedName, /*skipNonUniformPrimitiveFallback*/ true); + return isDiscriminantProperty(contextualType, p.symbol.escapedName); } return false; }), From 3c9b24b4a9edc49a6c87c8e6c9a204ad679c0e33 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 21:05:57 +0100 Subject: [PATCH 11/27] what if --- src/compiler/types.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cf9ea75b06cbe..8d9b3ea1ed393 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2756,7 +2756,7 @@ export interface ArrowFunction extends Expression, FunctionLikeDeclarationBase, readonly modifiers?: NodeArray; readonly equalsGreaterThanToken: EqualsGreaterThanToken; readonly body: ConciseBody; - readonly name: never; + readonly name?: never; } // The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 11a12e97d4b2e..783e3439dcb21 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4989,7 +4989,7 @@ declare namespace ts { readonly modifiers?: NodeArray; readonly equalsGreaterThanToken: EqualsGreaterThanToken; readonly body: ConciseBody; - readonly name: never; + readonly name?: never; } interface LiteralLikeNode extends Node { text: string; From 086bbe4ddcf0cc9743f31f93ebab62133ce73a9f Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 21:08:17 +0100 Subject: [PATCH 12/27] format --- src/compiler/checker.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2afaad2c00e8b..d110045470b12 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27285,22 +27285,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (type && type.flags & TypeFlags.Union) { const prop = getUnionOrIntersectionProperty(type as UnionType, name); if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { - const propType = getTypeOfSymbol(prop) + const propType = getTypeOfSymbol(prop); // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty if ((prop as TransientSymbol).links.isDiscriminantProperty === undefined) { - (prop as TransientSymbol).links.isDiscriminantProperty = { - 'true': undefined, - 'false': undefined - } + (prop as TransientSymbol).links.isDiscriminantProperty = { + true: undefined, + false: undefined, + }; } - const key = skipNonUniformPrimitiveFallback ? "true" : "false" + const key = skipNonUniformPrimitiveFallback ? "true" : "false"; - if((prop as TransientSymbol).links.isDiscriminantProperty![key] === undefined) { - (prop as TransientSymbol).links.isDiscriminantProperty![key] = - ((((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) - || !!(((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && !skipNonUniformPrimitiveFallback && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) - && !isGenericType(propType); + if ((prop as TransientSymbol).links.isDiscriminantProperty![key] === undefined) { + (prop as TransientSymbol).links.isDiscriminantProperty![key] = ((((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) + || !!(((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && !skipNonUniformPrimitiveFallback && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) + && !isGenericType(propType); } return !!(prop as TransientSymbol).links.isDiscriminantProperty![key]; } From f46d0dd566a57722f976e5dc18710aadeca52ec2 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 21:24:08 +0100 Subject: [PATCH 13/27] renaming, moving --- src/compiler/checker.ts | 21 +++++++++------------ src/compiler/types.ts | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d110045470b12..a3a26525684fa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27281,27 +27281,24 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } - function isDiscriminantProperty(type: Type | undefined, name: __String, skipNonUniformPrimitiveFallback: boolean = false) { + function isDiscriminantProperty(type: Type | undefined, name: __String, considerNonUniformPrimitivePropDiscriminant: boolean = true) { if (type && type.flags & TypeFlags.Union) { const prop = getUnionOrIntersectionProperty(type as UnionType, name); if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { const propType = getTypeOfSymbol(prop); // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty if ((prop as TransientSymbol).links.isDiscriminantProperty === undefined) { - (prop as TransientSymbol).links.isDiscriminantProperty = { - true: undefined, - false: undefined, - }; + (prop as TransientSymbol).links.isDiscriminantProperty = new Map(); } - const key = skipNonUniformPrimitiveFallback ? "true" : "false"; - - if ((prop as TransientSymbol).links.isDiscriminantProperty![key] === undefined) { - (prop as TransientSymbol).links.isDiscriminantProperty![key] = ((((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) - || !!(((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && !skipNonUniformPrimitiveFallback && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) + if (!(prop as TransientSymbol).links.isDiscriminantProperty!.has(considerNonUniformPrimitivePropDiscriminant)) { + const isDiscriminant = ((((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) + || !!(considerNonUniformPrimitivePropDiscriminant && ((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) && !isGenericType(propType); + (prop as TransientSymbol).links.isDiscriminantProperty!.set(considerNonUniformPrimitivePropDiscriminant, isDiscriminant); } - return !!(prop as TransientSymbol).links.isDiscriminantProperty![key]; + + return !!(prop as TransientSymbol).links.isDiscriminantProperty!.get(considerNonUniformPrimitivePropDiscriminant); } } return false; @@ -27310,7 +27307,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined { let result: Symbol[] | undefined; for (const sourceProperty of sourceProperties) { - if (isDiscriminantProperty(target, sourceProperty.escapedName, /*skipNonUniformPrimitiveFallback*/ true)) { + if (isDiscriminantProperty(target, sourceProperty.escapedName, /*considerNonUniformPrimitivePropDiscriminant*/ false)) { if (result) { result.push(sourceProperty); continue; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8d9b3ea1ed393..852fadc13bf7d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6020,7 +6020,7 @@ export interface SymbolLinks { leftSpread?: Symbol; // Left source for synthetic spread property rightSpread?: Symbol; // Right source for synthetic spread property syntheticOrigin?: Symbol; // For a property on a mapped or spread type, points back to the original property - isDiscriminantProperty?: { "true": boolean | undefined, "false": boolean | undefined }; // True if discriminant synthetic property + isDiscriminantProperty?: Map // True if discriminant synthetic property resolvedExports?: SymbolTable; // Resolved exports of module or combined early- and late-bound static members of a class. resolvedMembers?: SymbolTable; // Combined early- and late-bound members of a symbol exportsChecked?: boolean; // True if exports of external module have been checked From 8e0cf94e6cc4b2a20b705ed980af2db7691df8e8 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 6 Dec 2024 21:57:58 +0100 Subject: [PATCH 14/27] proper test name --- tests/baselines/reference/000.symbols | 81 ------------------- ...arrowUnionOfObjectsByPrimitiveProperty.js} | 24 +++--- ...wUnionOfObjectsByPrimitiveProperty.symbols | 80 ++++++++++++++++++ ...owUnionOfObjectsByPrimitiveProperty.types} | 63 +++++++-------- ...arrowUnionOfObjectsByPrimitiveProperty.ts} | 12 +-- 5 files changed, 130 insertions(+), 130 deletions(-) delete mode 100644 tests/baselines/reference/000.symbols rename tests/baselines/reference/{000.js => narrowUnionOfObjectsByPrimitiveProperty.js} (55%) create mode 100644 tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols rename tests/baselines/reference/{000.types => narrowUnionOfObjectsByPrimitiveProperty.types} (68%) rename tests/cases/compiler/{000.ts => narrowUnionOfObjectsByPrimitiveProperty.ts} (69%) diff --git a/tests/baselines/reference/000.symbols b/tests/baselines/reference/000.symbols deleted file mode 100644 index 73593f57591f5..0000000000000 --- a/tests/baselines/reference/000.symbols +++ /dev/null @@ -1,81 +0,0 @@ -//// [tests/cases/compiler/000.ts] //// - -=== 000.ts === -interface State { ->State : Symbol(State, Decl(000.ts, 0, 0)) ->Type : Symbol(Type, Decl(000.ts, 0, 16)) - - state: Type; ->state : Symbol(State.state, Decl(000.ts, 0, 23)) ->Type : Symbol(Type, Decl(000.ts, 0, 16)) -} - -interface UserName { ->UserName : Symbol(UserName, Decl(000.ts, 2, 1)) - - first: string; ->first : Symbol(UserName.first, Decl(000.ts, 4, 20)) - - last?: string; ->last : Symbol(UserName.last, Decl(000.ts, 5, 16)) -} - - -const nameState = {} as { ->nameState : Symbol(nameState, Decl(000.ts, 10, 5)) - - value: string; ->value : Symbol(value, Decl(000.ts, 10, 25)) - - state: State; ->state : Symbol(state, Decl(000.ts, 11, 16)) ->State : Symbol(State, Decl(000.ts, 0, 0)) - -} | { - value: UserName; ->value : Symbol(value, Decl(000.ts, 13, 5)) ->UserName : Symbol(UserName, Decl(000.ts, 2, 1)) - - state: State; ->state : Symbol(state, Decl(000.ts, 14, 18)) ->State : Symbol(State, Decl(000.ts, 0, 0)) ->UserName : Symbol(UserName, Decl(000.ts, 2, 1)) -} - -if (typeof nameState.value === "string") { ->nameState.value : Symbol(value, Decl(000.ts, 10, 25), Decl(000.ts, 13, 5)) ->nameState : Symbol(nameState, Decl(000.ts, 10, 5)) ->value : Symbol(value, Decl(000.ts, 10, 25), Decl(000.ts, 13, 5)) - - nameState.state satisfies State; ->nameState.state : Symbol(state, Decl(000.ts, 11, 16)) ->nameState : Symbol(nameState, Decl(000.ts, 10, 5)) ->state : Symbol(state, Decl(000.ts, 11, 16)) ->State : Symbol(State, Decl(000.ts, 0, 0)) - -} else { - nameState.state satisfies State; ->nameState.state : Symbol(state, Decl(000.ts, 14, 18)) ->nameState : Symbol(nameState, Decl(000.ts, 10, 5)) ->state : Symbol(state, Decl(000.ts, 14, 18)) ->State : Symbol(State, Decl(000.ts, 0, 0)) ->UserName : Symbol(UserName, Decl(000.ts, 2, 1)) -} - -declare const x: [string, number] | [number, string]; ->x : Symbol(x, Decl(000.ts, 24, 13)) - -if (typeof x[0] === "string") { ->x : Symbol(x, Decl(000.ts, 24, 13)) ->0 : Symbol(0) - - x[1] satisfies number; ->x : Symbol(x, Decl(000.ts, 24, 13)) ->1 : Symbol(1) - -} else { - x[1] satisfies string; ->x : Symbol(x, Decl(000.ts, 24, 13)) ->1 : Symbol(1) -} - diff --git a/tests/baselines/reference/000.js b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js similarity index 55% rename from tests/baselines/reference/000.js rename to tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js index f3048c0ac0398..85b874490aff2 100644 --- a/tests/baselines/reference/000.js +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js @@ -1,6 +1,6 @@ -//// [tests/cases/compiler/000.ts] //// +//// [tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts] //// -//// [000.ts] +//// [narrowUnionOfObjectsByPrimitiveProperty.ts] interface State { state: Type; } @@ -25,15 +25,15 @@ if (typeof nameState.value === "string") { nameState.state satisfies State; } -declare const x: [string, number] | [number, string]; -if (typeof x[0] === "string") { - x[1] satisfies number; +declare const arr: [string, number] | [number, string]; +if (typeof arr[0] === "string") { + arr[1] satisfies number; } else { - x[1] satisfies string; -} - + arr[1] satisfies string; +} -//// [000.js] +//// [narrowUnionOfObjectsByPrimitiveProperty.js] +"use strict"; var nameState = {}; if (typeof nameState.value === "string") { nameState.state; @@ -41,9 +41,9 @@ if (typeof nameState.value === "string") { else { nameState.state; } -if (typeof x[0] === "string") { - x[1]; +if (typeof arr[0] === "string") { + arr[1]; } else { - x[1]; + arr[1]; } diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols new file mode 100644 index 0000000000000..deb25784df524 --- /dev/null +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols @@ -0,0 +1,80 @@ +//// [tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts] //// + +=== narrowUnionOfObjectsByPrimitiveProperty.ts === +interface State { +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) +>Type : Symbol(Type, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 16)) + + state: Type; +>state : Symbol(State.state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 23)) +>Type : Symbol(Type, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 16)) +} + +interface UserName { +>UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) + + first: string; +>first : Symbol(UserName.first, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 4, 20)) + + last?: string; +>last : Symbol(UserName.last, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 5, 16)) +} + + +const nameState = {} as { +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 5)) + + value: string; +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 25)) + + state: State; +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 16)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) + +} | { + value: UserName; +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 5)) +>UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) + + state: State; +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 18)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) +>UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) +} + +if (typeof nameState.value === "string") { +>nameState.value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 5)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 5)) +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 5)) + + nameState.state satisfies State; +>nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 16)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 5)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 16)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) + +} else { + nameState.state satisfies State; +>nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 18)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 5)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 18)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) +>UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) +} + +declare const arr: [string, number] | [number, string]; +>arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) + +if (typeof arr[0] === "string") { +>arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) +>0 : Symbol(0) + + arr[1] satisfies number; +>arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) +>1 : Symbol(1) + +} else { + arr[1] satisfies string; +>arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) +>1 : Symbol(1) +} diff --git a/tests/baselines/reference/000.types b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types similarity index 68% rename from tests/baselines/reference/000.types rename to tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types index 86e58a694ded5..a247ebe0189a7 100644 --- a/tests/baselines/reference/000.types +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types @@ -1,6 +1,6 @@ -//// [tests/cases/compiler/000.ts] //// +//// [tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts] //// -=== 000.ts === +=== narrowUnionOfObjectsByPrimitiveProperty.ts === interface State { state: Type; >state : Type @@ -13,8 +13,8 @@ interface UserName { > : ^^^^^^ last?: string; ->last : string -> : ^^^^^^ +>last : string | undefined +> : ^^^^^^^^^^^^^^^^^^ } @@ -80,43 +80,42 @@ if (typeof nameState.value === "string") { > : ^^^^^^^^^^^^^^^ } -declare const x: [string, number] | [number, string]; ->x : [string, number] | [number, string] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -if (typeof x[0] === "string") { ->typeof x[0] === "string" : boolean -> : ^^^^^^^ ->typeof x[0] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->x[0] : string | number -> : ^^^^^^^^^^^^^^^ ->x : [string, number] | [number, string] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +declare const arr: [string, number] | [number, string]; +>arr : [string, number] | [number, string] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +if (typeof arr[0] === "string") { +>typeof arr[0] === "string" : boolean +> : ^^^^^^^ +>typeof arr[0] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>arr[0] : string | number +> : ^^^^^^^^^^^^^^^ +>arr : [string, number] | [number, string] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >0 : 0 > : ^ >"string" : "string" > : ^^^^^^^^ - x[1] satisfies number; ->x[1] satisfies number : number -> : ^^^^^^ ->x[1] : number -> : ^^^^^^ ->x : [string, number] -> : ^^^^^^^^^^^^^^^^ + arr[1] satisfies number; +>arr[1] satisfies number : number +> : ^^^^^^ +>arr[1] : number +> : ^^^^^^ +>arr : [string, number] +> : ^^^^^^^^^^^^^^^^ >1 : 1 > : ^ } else { - x[1] satisfies string; ->x[1] satisfies string : string -> : ^^^^^^ ->x[1] : string -> : ^^^^^^ ->x : [number, string] -> : ^^^^^^^^^^^^^^^^ + arr[1] satisfies string; +>arr[1] satisfies string : string +> : ^^^^^^ +>arr[1] : string +> : ^^^^^^ +>arr : [number, string] +> : ^^^^^^^^^^^^^^^^ >1 : 1 > : ^ } - diff --git a/tests/cases/compiler/000.ts b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts similarity index 69% rename from tests/cases/compiler/000.ts rename to tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts index bbf0b2409f72c..d698dcfb9c2b4 100644 --- a/tests/cases/compiler/000.ts +++ b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts @@ -1,3 +1,5 @@ +// @strict: true + interface State { state: Type; } @@ -22,9 +24,9 @@ if (typeof nameState.value === "string") { nameState.state satisfies State; } -declare const x: [string, number] | [number, string]; -if (typeof x[0] === "string") { - x[1] satisfies number; +declare const arr: [string, number] | [number, string]; +if (typeof arr[0] === "string") { + arr[1] satisfies number; } else { - x[1] satisfies string; -} + arr[1] satisfies string; +} \ No newline at end of file From e8741f22d7904410e3d824358ff6d6cd1200eb99 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Sat, 7 Dec 2024 07:55:37 +0100 Subject: [PATCH 15/27] add depLike test --- src/compiler/types.ts | 2 +- ...ionOfObjectsByPrimitiveProperty.errors.txt | 50 ++++++++ ...narrowUnionOfObjectsByPrimitiveProperty.js | 29 ++++- ...wUnionOfObjectsByPrimitiveProperty.symbols | 79 ++++++++++--- ...rowUnionOfObjectsByPrimitiveProperty.types | 109 +++++++++++++++++- ...narrowUnionOfObjectsByPrimitiveProperty.ts | 18 ++- 6 files changed, 266 insertions(+), 21 deletions(-) create mode 100644 tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 852fadc13bf7d..8f29782197a51 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6020,7 +6020,7 @@ export interface SymbolLinks { leftSpread?: Symbol; // Left source for synthetic spread property rightSpread?: Symbol; // Right source for synthetic spread property syntheticOrigin?: Symbol; // For a property on a mapped or spread type, points back to the original property - isDiscriminantProperty?: Map // True if discriminant synthetic property + isDiscriminantProperty?: Map // Key is a flag, value is true if discriminant synthetic property resolvedExports?: SymbolTable; // Resolved exports of module or combined early- and late-bound static members of a class. resolvedMembers?: SymbolTable; // Combined early- and late-bound members of a symbol exportsChecked?: boolean; // True if exports of external module have been checked diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt new file mode 100644 index 0000000000000..1af1f22837d67 --- /dev/null +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt @@ -0,0 +1,50 @@ +narrowUnionOfObjectsByPrimitiveProperty.ts(35,20): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. + + +==== narrowUnionOfObjectsByPrimitiveProperty.ts (1 errors) ==== + interface State { + state: Type; + } + + interface UserName { + first: string; + last?: string; + } + + const nameState = {} as { + value: string; + state: State; + } | { + value: UserName; + state: State; + } + + if (typeof nameState.value === "string") { + nameState.state satisfies State; + } else { + nameState.state satisfies State; + } + + + declare const arr: [string, number] | [number, string]; + if (typeof arr[0] === "string") { + arr[1] satisfies number; + } else { + arr[1] satisfies string; + } + + + function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { + if (typeof param.a === "string") { + return param.a.repeat(3); + ~~~~~~ +!!! error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. + } + if (typeof param.a === "number") { + return Math.exp(param.a); + } + throw new Error() + } + + aStringOrANumber({ a: "string" }) + aStringOrANumber({ a: 42 }) \ No newline at end of file diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js index 85b874490aff2..4a45eb58b556a 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js @@ -10,7 +10,6 @@ interface UserName { last?: string; } - const nameState = {} as { value: string; state: State; @@ -25,12 +24,27 @@ if (typeof nameState.value === "string") { nameState.state satisfies State; } + declare const arr: [string, number] | [number, string]; if (typeof arr[0] === "string") { arr[1] satisfies number; } else { arr[1] satisfies string; -} +} + + +function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { + if (typeof param.a === "string") { + return param.a.repeat(3); + } + if (typeof param.a === "number") { + return Math.exp(param.a); + } + throw new Error() +} + +aStringOrANumber({ a: "string" }) +aStringOrANumber({ a: 42 }) //// [narrowUnionOfObjectsByPrimitiveProperty.js] "use strict"; @@ -47,3 +61,14 @@ if (typeof arr[0] === "string") { else { arr[1]; } +function aStringOrANumber(param) { + if (typeof param.a === "string") { + return param.a.repeat(3); + } + if (typeof param.a === "number") { + return Math.exp(param.a); + } + throw new Error(); +} +aStringOrANumber({ a: "string" }); +aStringOrANumber({ a: 42 }); diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols index deb25784df524..d504f8f37e8b9 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols @@ -20,48 +20,48 @@ interface UserName { >last : Symbol(UserName.last, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 5, 16)) } - const nameState = {} as { ->nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 5)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 5)) value: string; ->value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 25)) +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 25)) state: State; ->state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 16)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 16)) >State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) } | { value: UserName; ->value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 5)) +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 5)) >UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) state: State; ->state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 18)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 18)) >State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) >UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) } if (typeof nameState.value === "string") { ->nameState.value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 5)) ->nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 5)) ->value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 5)) +>nameState.value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 5)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 5)) +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 5)) nameState.state satisfies State; ->nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 16)) ->nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 5)) ->state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 16)) +>nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 16)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 5)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 16)) >State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) } else { nameState.state satisfies State; ->nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 18)) ->nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 5)) ->state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 18)) +>nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 18)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 5)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 18)) >State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) >UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) } + declare const arr: [string, number] | [number, string]; >arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) @@ -78,3 +78,52 @@ if (typeof arr[0] === "string") { >arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) >1 : Symbol(1) } + + +function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { +>aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 29, 1)) +>T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 26)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) +>T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 26)) +>T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 26)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 89)) +>T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 26)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 124)) + + if (typeof param.a === "string") { +>param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) + + return param.a.repeat(3); +>param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37)) + } + if (typeof param.a === "number") { +>param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) + + return Math.exp(param.a); +>Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) +>param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) + } + throw new Error() +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +} + +aStringOrANumber({ a: "string" }) +>aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 29, 1)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 42, 18)) + +aStringOrANumber({ a: 42 }) +>aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 29, 1)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 43, 18)) + diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types index a247ebe0189a7..1a73393a326d4 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types @@ -17,7 +17,6 @@ interface UserName { > : ^^^^^^^^^^^^^^^^^^ } - const nameState = {} as { >nameState : { value: string; state: State; } | { value: UserName; state: State; } > : ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ @@ -80,6 +79,7 @@ if (typeof nameState.value === "string") { > : ^^^^^^^^^^^^^^^ } + declare const arr: [string, number] | [number, string]; >arr : [string, number] | [number, string] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -119,3 +119,110 @@ if (typeof arr[0] === "string") { >1 : 1 > : ^ } + + +function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { +>aStringOrANumber : (param: T) => T extends { a: string; } ? string : T extends { a: number; } ? number : never +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>a : string +> : ^^^^^^ +>a : number +> : ^^^^^^ +>param : T +> : ^ +>a : string +> : ^^^^^^ +>a : number +> : ^^^^^^ + + if (typeof param.a === "string") { +>typeof param.a === "string" : boolean +> : ^^^^^^^ +>typeof param.a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>param.a : string | number +> : ^^^^^^^^^^^^^^^ +>param : { a: string; } | { a: number; } +> : ^^^^^ ^^^^^^^^^^^ ^^^ +>a : string | number +> : ^^^^^^^^^^^^^^^ +>"string" : "string" +> : ^^^^^^^^ + + return param.a.repeat(3); +>param.a.repeat(3) : any +> : ^^^ +>param.a.repeat : any +> : ^^^ +>param.a : string +> : ^^^^^^ +>param : { a: string; } +> : ^^^^^ ^^^ +>a : string +> : ^^^^^^ +>repeat : any +> : ^^^ +>3 : 3 +> : ^ + } + if (typeof param.a === "number") { +>typeof param.a === "number" : boolean +> : ^^^^^^^ +>typeof param.a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>param.a : number +> : ^^^^^^ +>param : { a: number; } +> : ^^^^^ ^^^ +>a : number +> : ^^^^^^ +>"number" : "number" +> : ^^^^^^^^ + + return Math.exp(param.a); +>Math.exp(param.a) : number +> : ^^^^^^ +>Math.exp : (x: number) => number +> : ^ ^^ ^^^^^ +>Math : Math +> : ^^^^ +>exp : (x: number) => number +> : ^ ^^ ^^^^^ +>param.a : number +> : ^^^^^^ +>param : { a: number; } +> : ^^^^^ ^^^ +>a : number +> : ^^^^^^ + } + throw new Error() +>new Error() : Error +> : ^^^^^ +>Error : ErrorConstructor +> : ^^^^^^^^^^^^^^^^ +} + +aStringOrANumber({ a: "string" }) +>aStringOrANumber({ a: "string" }) : string +> : ^^^^^^ +>aStringOrANumber : (param: T) => T extends { a: string; } ? string : T extends { a: number; } ? number : never +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ a: "string" } : { a: string; } +> : ^^^^^^^^^^^^^^ +>a : string +> : ^^^^^^ +>"string" : "string" +> : ^^^^^^^^ + +aStringOrANumber({ a: 42 }) +>aStringOrANumber({ a: 42 }) : number +> : ^^^^^^ +>aStringOrANumber : (param: T) => T extends { a: string; } ? string : T extends { a: number; } ? number : never +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ a: 42 } : { a: number; } +> : ^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>42 : 42 +> : ^^ + diff --git a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts index d698dcfb9c2b4..3c941860c9242 100644 --- a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts +++ b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts @@ -9,7 +9,6 @@ interface UserName { last?: string; } - const nameState = {} as { value: string; state: State; @@ -24,9 +23,24 @@ if (typeof nameState.value === "string") { nameState.state satisfies State; } + declare const arr: [string, number] | [number, string]; if (typeof arr[0] === "string") { arr[1] satisfies number; } else { arr[1] satisfies string; -} \ No newline at end of file +} + + +function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { + if (typeof param.a === "string") { + return param.a.repeat(3); + } + if (typeof param.a === "number") { + return Math.exp(param.a); + } + throw new Error() +} + +aStringOrANumber({ a: "string" }) +aStringOrANumber({ a: 42 }) \ No newline at end of file From 443320aded4c95324338a72993a8bd615576e56f Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Sat, 7 Dec 2024 10:36:15 +0100 Subject: [PATCH 16/27] test to show interplay between cfa and assignability --- ...ionOfObjectsByPrimitiveProperty.errors.txt | 62 +++++- ...narrowUnionOfObjectsByPrimitiveProperty.js | 52 ++++- ...wUnionOfObjectsByPrimitiveProperty.symbols | 79 +++++++ ...rowUnionOfObjectsByPrimitiveProperty.types | 197 ++++++++++++++++++ ...narrowUnionOfObjectsByPrimitiveProperty.ts | 32 ++- 5 files changed, 418 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt index 1af1f22837d67..441eb03bdea9a 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt @@ -1,7 +1,19 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(35,20): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. +narrowUnionOfObjectsByPrimitiveProperty.ts(55,13): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. +narrowUnionOfObjectsByPrimitiveProperty.ts(60,1): error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. + Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: number; other: number; }'. + Types of property 'prop' are incompatible. + Type 'string | number' is not assignable to type 'number'. + Type 'string' is not assignable to type 'number'. +narrowUnionOfObjectsByPrimitiveProperty.ts(68,1): error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. + Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: number; other: number; }'. + Types of property 'prop' are incompatible. + Type 'string | number' is not assignable to type 'number'. + Type 'string' is not assignable to type 'number'. +narrowUnionOfObjectsByPrimitiveProperty.ts(71,14): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. -==== narrowUnionOfObjectsByPrimitiveProperty.ts (1 errors) ==== +==== narrowUnionOfObjectsByPrimitiveProperty.ts (5 errors) ==== interface State { state: Type; } @@ -47,4 +59,50 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(35,20): error TS2550: Property 'repea } aStringOrANumber({ a: "string" }) - aStringOrANumber({ a: 42 }) \ No newline at end of file + aStringOrANumber({ a: 42 }) + + + // The following two tests ensure that the discriminativeness of property 'prop' + // is treated differently in assignability and narrowing, and that the discriminativeness is properly cached. + declare let obj: { prop: string, other: string } | { prop: number, other: number } + + // Here, we first perform narrowing, but the subsequent assignability should not be affected. + // We expect an error there because of an incorrect value assigned to 'prop'. + // See contextualTypeWithUnionTypeObjectLiteral.ts + if(typeof obj.prop === "string") { + obj.other.repeat(3); + ~~~~~~ +!!! error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. + } else { + Math.exp(obj.other); + } + + obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } + ~~~ +!!! error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. +!!! error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: number; other: number; }'. +!!! error TS2322: Types of property 'prop' are incompatible. +!!! error TS2322: Type 'string | number' is not assignable to type 'number'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. + + + declare let obj2: { prop: string, other: string } | { prop: number, other: number } + + // Here, we first assign a value to 'obj2' and then perform narrowing. + // We expect an error here because of an incorrect value assigned to 'prop', like above, + // but the subsequent narrowing should not be affected by the assignability. + obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } + ~~~~ +!!! error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. +!!! error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: number; other: number; }'. +!!! error TS2322: Types of property 'prop' are incompatible. +!!! error TS2322: Type 'string | number' is not assignable to type 'number'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. + + if(typeof obj2.prop === "string") { + obj2.other.repeat(3); + ~~~~~~ +!!! error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. + } else { + Math.exp(obj2.other); + } \ No newline at end of file diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js index 4a45eb58b556a..4407b78276ee2 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js @@ -44,7 +44,37 @@ function aStringOrANumber(param: T): T } aStringOrANumber({ a: "string" }) -aStringOrANumber({ a: 42 }) +aStringOrANumber({ a: 42 }) + + +// The following two tests ensure that the discriminativeness of property 'prop' +// is treated differently in assignability and narrowing, and that the discriminativeness is properly cached. +declare let obj: { prop: string, other: string } | { prop: number, other: number } + +// Here, we first perform narrowing, but the subsequent assignability should not be affected. +// We expect an error there because of an incorrect value assigned to 'prop'. +// See contextualTypeWithUnionTypeObjectLiteral.ts +if(typeof obj.prop === "string") { + obj.other.repeat(3); +} else { + Math.exp(obj.other); +} + +obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } + + +declare let obj2: { prop: string, other: string } | { prop: number, other: number } + +// Here, we first assign a value to 'obj2' and then perform narrowing. +// We expect an error here because of an incorrect value assigned to 'prop', like above, +// but the subsequent narrowing should not be affected by the assignability. +obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } + +if(typeof obj2.prop === "string") { + obj2.other.repeat(3); +} else { + Math.exp(obj2.other); +} //// [narrowUnionOfObjectsByPrimitiveProperty.js] "use strict"; @@ -72,3 +102,23 @@ function aStringOrANumber(param) { } aStringOrANumber({ a: "string" }); aStringOrANumber({ a: 42 }); +// Here, we first perform narrowing, but the subsequent assignability should not be affected. +// We expect an error there because of an incorrect value assigned to 'prop'. +// See contextualTypeWithUnionTypeObjectLiteral.ts +if (typeof obj.prop === "string") { + obj.other.repeat(3); +} +else { + Math.exp(obj.other); +} +obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" }; +// Here, we first assign a value to 'obj2' and then perform narrowing. +// We expect an error here because of an incorrect value assigned to 'prop', like above, +// but the subsequent narrowing should not be affected by the assignability. +obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" }; +if (typeof obj2.prop === "string") { + obj2.other.repeat(3); +} +else { + Math.exp(obj2.other); +} diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols index d504f8f37e8b9..7b2cab8970062 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols @@ -127,3 +127,82 @@ aStringOrANumber({ a: 42 }) >aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 29, 1)) >a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 43, 18)) + +// The following two tests ensure that the discriminativeness of property 'prop' +// is treated differently in assignability and narrowing, and that the discriminativeness is properly cached. +declare let obj: { prop: string, other: string } | { prop: number, other: number } +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 18)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 32)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 52)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 66)) + +// Here, we first perform narrowing, but the subsequent assignability should not be affected. +// We expect an error there because of an incorrect value assigned to 'prop'. +// See contextualTypeWithUnionTypeObjectLiteral.ts +if(typeof obj.prop === "string") { +>obj.prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 52)) +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 52)) + + obj.other.repeat(3); +>obj.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 32)) +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 32)) + +} else { + Math.exp(obj.other); +>Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) +>obj.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 66)) +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 66)) +} + +obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 59, 7)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 59, 52)) + + +declare let obj2: { prop: string, other: string } | { prop: number, other: number } +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 19)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 33)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 53)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 67)) + +// Here, we first assign a value to 'obj2' and then perform narrowing. +// We expect an error here because of an incorrect value assigned to 'prop', like above, +// but the subsequent narrowing should not be affected by the assignability. +obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 67, 8)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 67, 53)) + +if(typeof obj2.prop === "string") { +>obj2.prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 19), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 53)) +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 19), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 53)) + + obj2.other.repeat(3); +>obj2.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 33)) +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 33)) + +} else { + Math.exp(obj2.other); +>Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) +>obj2.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 67)) +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 67)) +} diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types index 1a73393a326d4..31c28939559ce 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types @@ -226,3 +226,200 @@ aStringOrANumber({ a: 42 }) >42 : 42 > : ^^ + +// The following two tests ensure that the discriminativeness of property 'prop' +// is treated differently in assignability and narrowing, and that the discriminativeness is properly cached. +declare let obj: { prop: string, other: string } | { prop: number, other: number } +>obj : { prop: string; other: string; } | { prop: number; other: number; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>prop : string +> : ^^^^^^ +>other : string +> : ^^^^^^ +>prop : number +> : ^^^^^^ +>other : number +> : ^^^^^^ + +// Here, we first perform narrowing, but the subsequent assignability should not be affected. +// We expect an error there because of an incorrect value assigned to 'prop'. +// See contextualTypeWithUnionTypeObjectLiteral.ts +if(typeof obj.prop === "string") { +>typeof obj.prop === "string" : boolean +> : ^^^^^^^ +>typeof obj.prop : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>obj.prop : string | number +> : ^^^^^^^^^^^^^^^ +>obj : { prop: string; other: string; } | { prop: number; other: number; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>prop : string | number +> : ^^^^^^^^^^^^^^^ +>"string" : "string" +> : ^^^^^^^^ + + obj.other.repeat(3); +>obj.other.repeat(3) : any +> : ^^^ +>obj.other.repeat : any +> : ^^^ +>obj.other : string +> : ^^^^^^ +>obj : { prop: string; other: string; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^ +>other : string +> : ^^^^^^ +>repeat : any +> : ^^^ +>3 : 3 +> : ^ + +} else { + Math.exp(obj.other); +>Math.exp(obj.other) : number +> : ^^^^^^ +>Math.exp : (x: number) => number +> : ^ ^^ ^^^^^ +>Math : Math +> : ^^^^ +>exp : (x: number) => number +> : ^ ^^ ^^^^^ +>obj.other : number +> : ^^^^^^ +>obj : { prop: number; other: number; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^ +>other : number +> : ^^^^^^ +} + +obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } +>obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } : { prop: string | number; other: never; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ +>obj : { prop: string; other: string; } | { prop: number; other: number; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>{ prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } : { prop: string | number; other: never; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ +>prop : string | number +> : ^^^^^^^^^^^^^^^ +>Math.random() > 0.5 ? "whatever" : 42 : 42 | "whatever" +> : ^^^^^^^^^^^^^^^ +>Math.random() > 0.5 : boolean +> : ^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>0.5 : 0.5 +> : ^^^ +>"whatever" : "whatever" +> : ^^^^^^^^^^ +>42 : 42 +> : ^^ +>other : never +> : ^^^^^ +>"irrelevant" as never : never +> : ^^^^^ +>"irrelevant" : "irrelevant" +> : ^^^^^^^^^^^^ + + +declare let obj2: { prop: string, other: string } | { prop: number, other: number } +>obj2 : { prop: string; other: string; } | { prop: number; other: number; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>prop : string +> : ^^^^^^ +>other : string +> : ^^^^^^ +>prop : number +> : ^^^^^^ +>other : number +> : ^^^^^^ + +// Here, we first assign a value to 'obj2' and then perform narrowing. +// We expect an error here because of an incorrect value assigned to 'prop', like above, +// but the subsequent narrowing should not be affected by the assignability. +obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } +>obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } : { prop: string | number; other: never; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ +>obj2 : { prop: string; other: string; } | { prop: number; other: number; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>{ prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } : { prop: string | number; other: never; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ +>prop : string | number +> : ^^^^^^^^^^^^^^^ +>Math.random() > 0.5 ? "whatever" : 42 : 42 | "whatever" +> : ^^^^^^^^^^^^^^^ +>Math.random() > 0.5 : boolean +> : ^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>0.5 : 0.5 +> : ^^^ +>"whatever" : "whatever" +> : ^^^^^^^^^^ +>42 : 42 +> : ^^ +>other : never +> : ^^^^^ +>"irrelevant" as never : never +> : ^^^^^ +>"irrelevant" : "irrelevant" +> : ^^^^^^^^^^^^ + +if(typeof obj2.prop === "string") { +>typeof obj2.prop === "string" : boolean +> : ^^^^^^^ +>typeof obj2.prop : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>obj2.prop : string | number +> : ^^^^^^^^^^^^^^^ +>obj2 : { prop: string; other: string; } | { prop: number; other: number; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^ +>prop : string | number +> : ^^^^^^^^^^^^^^^ +>"string" : "string" +> : ^^^^^^^^ + + obj2.other.repeat(3); +>obj2.other.repeat(3) : any +> : ^^^ +>obj2.other.repeat : any +> : ^^^ +>obj2.other : string +> : ^^^^^^ +>obj2 : { prop: string; other: string; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^ +>other : string +> : ^^^^^^ +>repeat : any +> : ^^^ +>3 : 3 +> : ^ + +} else { + Math.exp(obj2.other); +>Math.exp(obj2.other) : number +> : ^^^^^^ +>Math.exp : (x: number) => number +> : ^ ^^ ^^^^^ +>Math : Math +> : ^^^^ +>exp : (x: number) => number +> : ^ ^^ ^^^^^ +>obj2.other : number +> : ^^^^^^ +>obj2 : { prop: number; other: number; } +> : ^^^^^^^^ ^^^^^^^^^ ^^^ +>other : number +> : ^^^^^^ +} diff --git a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts index 3c941860c9242..15e9551db162b 100644 --- a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts +++ b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts @@ -43,4 +43,34 @@ function aStringOrANumber(param: T): T } aStringOrANumber({ a: "string" }) -aStringOrANumber({ a: 42 }) \ No newline at end of file +aStringOrANumber({ a: 42 }) + + +// The following two tests ensure that the discriminativeness of property 'prop' +// is treated differently in assignability and narrowing, and that the discriminativeness is properly cached. +declare let obj: { prop: string, other: string } | { prop: number, other: number } + +// Here, we first perform narrowing, but the subsequent assignability should not be affected. +// We expect an error there because of an incorrect value assigned to 'prop'. +// See contextualTypeWithUnionTypeObjectLiteral.ts +if(typeof obj.prop === "string") { + obj.other.repeat(3); +} else { + Math.exp(obj.other); +} + +obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } + + +declare let obj2: { prop: string, other: string } | { prop: number, other: number } + +// Here, we first assign a value to 'obj2' and then perform narrowing. +// We expect an error here because of an incorrect value assigned to 'prop', like above, +// but the subsequent narrowing should not be affected by the assignability. +obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } + +if(typeof obj2.prop === "string") { + obj2.other.repeat(3); +} else { + Math.exp(obj2.other); +} \ No newline at end of file From 81b5d4a3689c72ce5678e364de35895094a283a7 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 9 Dec 2024 17:09:09 +0100 Subject: [PATCH 17/27] cleaner faster code --- src/compiler/checker.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 458f4519414d7..7d2ad12f0e383 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27287,18 +27287,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { const propType = getTypeOfSymbol(prop); // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty - if ((prop as TransientSymbol).links.isDiscriminantProperty === undefined) { - (prop as TransientSymbol).links.isDiscriminantProperty = new Map(); - } + const transientSymbol = prop as TransientSymbol; + transientSymbol.links.isDiscriminantProperty ??= new Map(); + + let isDiscriminant = transientSymbol.links.isDiscriminantProperty.get(considerNonUniformPrimitivePropDiscriminant); - if (!(prop as TransientSymbol).links.isDiscriminantProperty!.has(considerNonUniformPrimitivePropDiscriminant)) { - const isDiscriminant = ((((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) - || !!(considerNonUniformPrimitivePropDiscriminant && ((prop as TransientSymbol).links.checkFlags & CheckFlags.HasNonUniformType) && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) + if (typeof isDiscriminant === "undefined") { + isDiscriminant = (((transientSymbol.links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) + || !!(considerNonUniformPrimitivePropDiscriminant && (transientSymbol.links.checkFlags & CheckFlags.HasNonUniformType) && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) && !isGenericType(propType); - (prop as TransientSymbol).links.isDiscriminantProperty!.set(considerNonUniformPrimitivePropDiscriminant, isDiscriminant); + + transientSymbol.links.isDiscriminantProperty.set(considerNonUniformPrimitivePropDiscriminant, isDiscriminant); } - return !!(prop as TransientSymbol).links.isDiscriminantProperty!.get(considerNonUniformPrimitivePropDiscriminant); + return isDiscriminant; } } return false; From 6c0d0854bb53c074251ca105b180d14df809ef61 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 9 Dec 2024 17:38:32 +0100 Subject: [PATCH 18/27] add comment --- src/compiler/checker.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7d2ad12f0e383..a098f8c835151 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27281,6 +27281,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } + // The flag `considerNonUniformPrimitivePropDiscriminant`, when enabled, introduces a new criterion for a property to be considered discriminant: + // 1 The property must be non-uniform + // 2 The property must include at least one primitive type as a possible type function isDiscriminantProperty(type: Type | undefined, name: __String, considerNonUniformPrimitivePropDiscriminant: boolean = true) { if (type && type.flags & TypeFlags.Union) { const prop = getUnionOrIntersectionProperty(type as UnionType, name); From 5566306dbd446712a9e614f664b312b676810c69 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 9 Dec 2024 22:47:39 +0100 Subject: [PATCH 19/27] add some repros --- ...ionOfObjectsByPrimitiveProperty.errors.txt | 104 ++++- ...narrowUnionOfObjectsByPrimitiveProperty.js | 124 ------ ...wUnionOfObjectsByPrimitiveProperty.symbols | 406 ++++++++++++++---- ...rowUnionOfObjectsByPrimitiveProperty.types | 322 ++++++++++++++ ...narrowUnionOfObjectsByPrimitiveProperty.ts | 91 +++- 5 files changed, 823 insertions(+), 224 deletions(-) delete mode 100644 tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt index 441eb03bdea9a..e1fecffb6c46c 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt @@ -1,19 +1,22 @@ -narrowUnionOfObjectsByPrimitiveProperty.ts(35,20): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. -narrowUnionOfObjectsByPrimitiveProperty.ts(55,13): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. -narrowUnionOfObjectsByPrimitiveProperty.ts(60,1): error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. +narrowUnionOfObjectsByPrimitiveProperty.ts(37,20): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. +narrowUnionOfObjectsByPrimitiveProperty.ts(57,13): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. +narrowUnionOfObjectsByPrimitiveProperty.ts(62,1): error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: number; other: number; }'. Types of property 'prop' are incompatible. Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'. -narrowUnionOfObjectsByPrimitiveProperty.ts(68,1): error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. +narrowUnionOfObjectsByPrimitiveProperty.ts(70,1): error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: number; other: number; }'. Types of property 'prop' are incompatible. Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'. -narrowUnionOfObjectsByPrimitiveProperty.ts(71,14): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. +narrowUnionOfObjectsByPrimitiveProperty.ts(73,14): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. +narrowUnionOfObjectsByPrimitiveProperty.ts(98,32): error TS2339: Property 'label' does not exist on type 'never'. -==== narrowUnionOfObjectsByPrimitiveProperty.ts (5 errors) ==== +==== narrowUnionOfObjectsByPrimitiveProperty.ts (6 errors) ==== + export {} + interface State { state: Type; } @@ -105,4 +108,91 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(71,14): error TS2550: Property 'repea !!! error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. } else { Math.exp(obj2.other); - } \ No newline at end of file + } + + + interface ILocalizedString { + original: string; + value: string; + } + + type Opt = ({ + label: ILocalizedString; + alias?: string; + } | { + label: string; + alias: string; + }) + + declare const opt: Opt + + if (typeof opt.label === 'string') { + opt + // ^? + const l = opt.label; + const a = opt.alias ?? opt.label; + ~~~~~ +!!! error TS2339: Property 'label' does not exist on type 'never'. + } else { + opt + // ^? + const l = opt.label; + const a = opt.alias ?? opt.label.original; + } + + + type PackageDetails = { + docsLink?: string; + docsLinkWarning?: string; + title?: string; + description?: string; + repo?: string; + license?: string; + }; + + type PackageDetailsSuccess = PackageDetails & { + docsLink: string; + }; + + interface FilePathAndName { + path: string; + name: string; + } + + interface PackageFilePathAndName extends FilePathAndName { + packageRegistry: string; // e.g. npm, pypi + } + + type ParsedPackageInfo = { + name: string; + packageFile: PackageFilePathAndName; + language: string; + version: string; + }; + + + type PackageDocsResult = { + packageInfo: ParsedPackageInfo; + } & ({ + error: string; + details?: never; + } | { + details: PackageDetailsSuccess; + error?: never; + }) + + declare const filtered: PackageDocsResult[]; + + filtered.sort((a, b) => { + const rank = (result: PackageDocsResult) => { + result.details + if (result.error) { + return 2; + } else if (result.details?.docsLinkWarning) { + return 1; + } else { + return 0; + } + }; + return rank(a) - rank(b); + }); \ No newline at end of file diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js deleted file mode 100644 index 4407b78276ee2..0000000000000 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js +++ /dev/null @@ -1,124 +0,0 @@ -//// [tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts] //// - -//// [narrowUnionOfObjectsByPrimitiveProperty.ts] -interface State { - state: Type; -} - -interface UserName { - first: string; - last?: string; -} - -const nameState = {} as { - value: string; - state: State; -} | { - value: UserName; - state: State; -} - -if (typeof nameState.value === "string") { - nameState.state satisfies State; -} else { - nameState.state satisfies State; -} - - -declare const arr: [string, number] | [number, string]; -if (typeof arr[0] === "string") { - arr[1] satisfies number; -} else { - arr[1] satisfies string; -} - - -function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { - if (typeof param.a === "string") { - return param.a.repeat(3); - } - if (typeof param.a === "number") { - return Math.exp(param.a); - } - throw new Error() -} - -aStringOrANumber({ a: "string" }) -aStringOrANumber({ a: 42 }) - - -// The following two tests ensure that the discriminativeness of property 'prop' -// is treated differently in assignability and narrowing, and that the discriminativeness is properly cached. -declare let obj: { prop: string, other: string } | { prop: number, other: number } - -// Here, we first perform narrowing, but the subsequent assignability should not be affected. -// We expect an error there because of an incorrect value assigned to 'prop'. -// See contextualTypeWithUnionTypeObjectLiteral.ts -if(typeof obj.prop === "string") { - obj.other.repeat(3); -} else { - Math.exp(obj.other); -} - -obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } - - -declare let obj2: { prop: string, other: string } | { prop: number, other: number } - -// Here, we first assign a value to 'obj2' and then perform narrowing. -// We expect an error here because of an incorrect value assigned to 'prop', like above, -// but the subsequent narrowing should not be affected by the assignability. -obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } - -if(typeof obj2.prop === "string") { - obj2.other.repeat(3); -} else { - Math.exp(obj2.other); -} - -//// [narrowUnionOfObjectsByPrimitiveProperty.js] -"use strict"; -var nameState = {}; -if (typeof nameState.value === "string") { - nameState.state; -} -else { - nameState.state; -} -if (typeof arr[0] === "string") { - arr[1]; -} -else { - arr[1]; -} -function aStringOrANumber(param) { - if (typeof param.a === "string") { - return param.a.repeat(3); - } - if (typeof param.a === "number") { - return Math.exp(param.a); - } - throw new Error(); -} -aStringOrANumber({ a: "string" }); -aStringOrANumber({ a: 42 }); -// Here, we first perform narrowing, but the subsequent assignability should not be affected. -// We expect an error there because of an incorrect value assigned to 'prop'. -// See contextualTypeWithUnionTypeObjectLiteral.ts -if (typeof obj.prop === "string") { - obj.other.repeat(3); -} -else { - Math.exp(obj.other); -} -obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" }; -// Here, we first assign a value to 'obj2' and then perform narrowing. -// We expect an error here because of an incorrect value assigned to 'prop', like above, -// but the subsequent narrowing should not be affected by the assignability. -obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" }; -if (typeof obj2.prop === "string") { - obj2.other.repeat(3); -} -else { - Math.exp(obj2.other); -} diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols index 7b2cab8970062..0cf87d505e958 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols @@ -1,208 +1,430 @@ //// [tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts] //// === narrowUnionOfObjectsByPrimitiveProperty.ts === +export {} + interface State { ->State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) ->Type : Symbol(Type, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 16)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 9)) +>Type : Symbol(Type, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 16)) state: Type; ->state : Symbol(State.state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 23)) ->Type : Symbol(Type, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 16)) +>state : Symbol(State.state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 23)) +>Type : Symbol(Type, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 16)) } interface UserName { ->UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) +>UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 4, 1)) first: string; ->first : Symbol(UserName.first, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 4, 20)) +>first : Symbol(UserName.first, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 6, 20)) last?: string; ->last : Symbol(UserName.last, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 5, 16)) +>last : Symbol(UserName.last, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 7, 16)) } const nameState = {} as { ->nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 5)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 5)) value: string; ->value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 25)) +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 25)) state: State; ->state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 16)) ->State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 16)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 9)) } | { value: UserName; ->value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 5)) ->UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 5)) +>UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 4, 1)) state: State; ->state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 18)) ->State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) ->UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 15, 18)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 9)) +>UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 4, 1)) } if (typeof nameState.value === "string") { ->nameState.value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 5)) ->nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 5)) ->value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 5)) +>nameState.value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 5)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 5)) +>value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 5)) nameState.state satisfies State; ->nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 16)) ->nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 5)) ->state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 10, 16)) ->State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) +>nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 16)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 5)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 16)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 9)) } else { nameState.state satisfies State; ->nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 18)) ->nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 9, 5)) ->state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 13, 18)) ->State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 0)) ->UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 2, 1)) +>nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 15, 18)) +>nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 5)) +>state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 15, 18)) +>State : Symbol(State, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 0, 9)) +>UserName : Symbol(UserName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 4, 1)) } declare const arr: [string, number] | [number, string]; ->arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) +>arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 26, 13)) if (typeof arr[0] === "string") { ->arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) +>arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 26, 13)) >0 : Symbol(0) arr[1] satisfies number; ->arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) +>arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 26, 13)) >1 : Symbol(1) } else { arr[1] satisfies string; ->arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 24, 13)) +>arr : Symbol(arr, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 26, 13)) >1 : Symbol(1) } function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { ->aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 29, 1)) ->T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 26)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) ->param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) ->T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 26)) ->T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 26)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 89)) ->T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 26)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 124)) +>aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 31, 1)) +>T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 26)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 37)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 67)) +>T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 26)) +>T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 26)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 89)) +>T : Symbol(T, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 26)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 124)) if (typeof param.a === "string") { ->param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) ->param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) +>param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 37), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 67)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 37), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) return param.a.repeat(3); ->param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37)) ->param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 37)) +>param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 37)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 67)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 37)) } if (typeof param.a === "number") { ->param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) ->param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) +>param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 67)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) return Math.exp(param.a); >Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) >Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) ->param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) ->param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 67)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 32, 53)) +>param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) +>param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 67)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) } throw new Error() >Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) } aStringOrANumber({ a: "string" }) ->aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 29, 1)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 42, 18)) +>aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 31, 1)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 44, 18)) aStringOrANumber({ a: 42 }) ->aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 29, 1)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 43, 18)) +>aStringOrANumber : Symbol(aStringOrANumber, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 31, 1)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 45, 18)) // The following two tests ensure that the discriminativeness of property 'prop' // is treated differently in assignability and narrowing, and that the discriminativeness is properly cached. declare let obj: { prop: string, other: string } | { prop: number, other: number } ->obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) ->prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 18)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 32)) ->prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 52)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 66)) +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 18)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 32)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 52)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 66)) // Here, we first perform narrowing, but the subsequent assignability should not be affected. // We expect an error there because of an incorrect value assigned to 'prop'. // See contextualTypeWithUnionTypeObjectLiteral.ts if(typeof obj.prop === "string") { ->obj.prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 52)) ->obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) ->prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 52)) +>obj.prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 52)) +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 52)) obj.other.repeat(3); ->obj.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 32)) ->obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 32)) +>obj.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 32)) +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 11)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 32)) } else { Math.exp(obj.other); >Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) >Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) ->obj.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 66)) ->obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 66)) +>obj.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 66)) +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 11)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 66)) } obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } ->obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 48, 11)) ->prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 59, 7)) +>obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 61, 7)) >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) >Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 59, 52)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 61, 52)) declare let obj2: { prop: string, other: string } | { prop: number, other: number } ->obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) ->prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 19)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 33)) ->prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 53)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 67)) +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 19)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 33)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 53)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 67)) // Here, we first assign a value to 'obj2' and then perform narrowing. // We expect an error here because of an incorrect value assigned to 'prop', like above, // but the subsequent narrowing should not be affected by the assignability. obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } ->obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) ->prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 67, 8)) +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 69, 8)) >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) >Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 67, 53)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 69, 53)) if(typeof obj2.prop === "string") { ->obj2.prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 19), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 53)) ->obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) ->prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 19), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 53)) +>obj2.prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 19), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 53)) +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 11)) +>prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 19), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 53)) obj2.other.repeat(3); ->obj2.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 33)) ->obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 33)) +>obj2.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 33)) +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 11)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 33)) } else { Math.exp(obj2.other); >Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) >Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) ->obj2.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 67)) ->obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 11)) ->other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 62, 67)) +>obj2.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 67)) +>obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 11)) +>other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 67)) } + + +interface ILocalizedString { +>ILocalizedString : Symbol(ILocalizedString, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 75, 1)) + + original: string; +>original : Symbol(ILocalizedString.original, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 78, 28)) + + value: string; +>value : Symbol(ILocalizedString.value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 79, 21)) +} + +type Opt = ({ +>Opt : Symbol(Opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 81, 1)) + + label: ILocalizedString; +>label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13)) +>ILocalizedString : Symbol(ILocalizedString, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 75, 1)) + + alias?: string; +>alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 84, 28)) + +} | { + label: string; +>label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 86, 5)) + + alias: string; +>alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 87, 18)) + +}) + +declare const opt: Opt +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) +>Opt : Symbol(Opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 81, 1)) + +if (typeof opt.label === 'string') { +>opt.label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 86, 5)) +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) +>label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 86, 5)) + + opt +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) + + // ^? + const l = opt.label; +>l : Symbol(l, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 96, 9)) +>opt.label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 86, 5)) +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) +>label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 86, 5)) + + const a = opt.alias ?? opt.label; +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 97, 9)) +>opt.alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 87, 18)) +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) +>alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 87, 18)) +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) + +} else { + opt +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) + + // ^? + const l = opt.label; +>l : Symbol(l, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 101, 9)) +>opt.label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13)) +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) +>label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13)) + + const a = opt.alias ?? opt.label.original; +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 102, 9)) +>opt.alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 84, 28)) +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) +>alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 84, 28)) +>opt.label.original : Symbol(ILocalizedString.original, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 78, 28)) +>opt.label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13)) +>opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) +>label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13)) +>original : Symbol(ILocalizedString.original, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 78, 28)) +} + + +type PackageDetails = { +>PackageDetails : Symbol(PackageDetails, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 103, 1)) + + docsLink?: string; +>docsLink : Symbol(docsLink, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 106, 23)) + + docsLinkWarning?: string; +>docsLinkWarning : Symbol(docsLinkWarning, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 107, 20)) + + title?: string; +>title : Symbol(title, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 108, 27)) + + description?: string; +>description : Symbol(description, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 109, 17)) + + repo?: string; +>repo : Symbol(repo, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 110, 23)) + + license?: string; +>license : Symbol(license, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 111, 16)) + +}; + +type PackageDetailsSuccess = PackageDetails & { +>PackageDetailsSuccess : Symbol(PackageDetailsSuccess, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 113, 2)) +>PackageDetails : Symbol(PackageDetails, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 103, 1)) + + docsLink: string; +>docsLink : Symbol(docsLink, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 115, 47)) + +}; + +interface FilePathAndName { +>FilePathAndName : Symbol(FilePathAndName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 117, 2)) + + path: string; +>path : Symbol(FilePathAndName.path, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 119, 27)) + + name: string; +>name : Symbol(FilePathAndName.name, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 120, 15)) +} + +interface PackageFilePathAndName extends FilePathAndName { +>PackageFilePathAndName : Symbol(PackageFilePathAndName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 122, 1)) +>FilePathAndName : Symbol(FilePathAndName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 117, 2)) + + packageRegistry: string; // e.g. npm, pypi +>packageRegistry : Symbol(PackageFilePathAndName.packageRegistry, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 124, 58)) +} + +type ParsedPackageInfo = { +>ParsedPackageInfo : Symbol(ParsedPackageInfo, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 126, 1)) + + name: string; +>name : Symbol(name, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 128, 26)) + + packageFile: PackageFilePathAndName; +>packageFile : Symbol(packageFile, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 129, 15)) +>PackageFilePathAndName : Symbol(PackageFilePathAndName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 122, 1)) + + language: string; +>language : Symbol(language, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 130, 38)) + + version: string; +>version : Symbol(version, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 131, 19)) + +}; + + +type PackageDocsResult = { +>PackageDocsResult : Symbol(PackageDocsResult, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 133, 2)) + + packageInfo: ParsedPackageInfo; +>packageInfo : Symbol(packageInfo, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 136, 26)) +>ParsedPackageInfo : Symbol(ParsedPackageInfo, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 126, 1)) + +} & ({ + error: string; +>error : Symbol(error, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 138, 6)) + + details?: never; +>details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18)) + +} | { + details: PackageDetailsSuccess; +>details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) +>PackageDetailsSuccess : Symbol(PackageDetailsSuccess, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 113, 2)) + + error?: never; +>error : Symbol(error, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 142, 35)) + +}) + +declare const filtered: PackageDocsResult[]; +>filtered : Symbol(filtered, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 146, 13)) +>PackageDocsResult : Symbol(PackageDocsResult, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 133, 2)) + +filtered.sort((a, b) => { +>filtered.sort : Symbol(Array.sort, Decl(lib.es5.d.ts, --, --)) +>filtered : Symbol(filtered, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 146, 13)) +>sort : Symbol(Array.sort, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 148, 15)) +>b : Symbol(b, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 148, 17)) + + const rank = (result: PackageDocsResult) => { +>rank : Symbol(rank, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 7)) +>result : Symbol(result, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 16)) +>PackageDocsResult : Symbol(PackageDocsResult, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 133, 2)) + + result.details +>result.details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) +>result : Symbol(result, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 16)) +>details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) + + if (result.error) { +>result.error : Symbol(error, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 138, 6), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 142, 35)) +>result : Symbol(result, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 16)) +>error : Symbol(error, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 138, 6), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 142, 35)) + + return 2; + } else if (result.details?.docsLinkWarning) { +>result.details?.docsLinkWarning : Symbol(docsLinkWarning, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 107, 20)) +>result.details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) +>result : Symbol(result, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 16)) +>details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) +>docsLinkWarning : Symbol(docsLinkWarning, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 107, 20)) + + return 1; + } else { + return 0; + } + }; + return rank(a) - rank(b); +>rank : Symbol(rank, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 7)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 148, 15)) +>rank : Symbol(rank, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 7)) +>b : Symbol(b, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 148, 17)) + +}); diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types index 31c28939559ce..b98869dc7eafe 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types @@ -1,6 +1,8 @@ //// [tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts] //// === narrowUnionOfObjectsByPrimitiveProperty.ts === +export {} + interface State { state: Type; >state : Type @@ -423,3 +425,323 @@ if(typeof obj2.prop === "string") { >other : number > : ^^^^^^ } + + +interface ILocalizedString { + original: string; +>original : string +> : ^^^^^^ + + value: string; +>value : string +> : ^^^^^^ +} + +type Opt = ({ +>Opt : Opt +> : ^^^ + + label: ILocalizedString; +>label : ILocalizedString +> : ^^^^^^^^^^^^^^^^ + + alias?: string; +>alias : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + +} | { + label: string; +>label : string +> : ^^^^^^ + + alias: string; +>alias : string +> : ^^^^^^ + +}) + +declare const opt: Opt +>opt : Opt +> : ^^^ + +if (typeof opt.label === 'string') { +>typeof opt.label === 'string' : boolean +> : ^^^^^^^ +>typeof opt.label : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>opt.label : string | ILocalizedString +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>opt : Opt +> : ^^^ +>label : string | ILocalizedString +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>'string' : "string" +> : ^^^^^^^^ + + opt +>opt : { label: string; alias: string; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^ + + // ^? + const l = opt.label; +>l : string +> : ^^^^^^ +>opt.label : string +> : ^^^^^^ +>opt : { label: string; alias: string; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^ +>label : string +> : ^^^^^^ + + const a = opt.alias ?? opt.label; +>a : string +> : ^^^^^^ +>opt.alias ?? opt.label : string +> : ^^^^^^ +>opt.alias : string +> : ^^^^^^ +>opt : { label: string; alias: string; } +> : ^^^^^^^^^ ^^^^^^^^^ ^^^ +>alias : string +> : ^^^^^^ +>opt.label : any +> : ^^^ +>opt : never +> : ^^^^^ +>label : any +> : ^^^ + +} else { + opt +>opt : { label: ILocalizedString; alias?: string; } +> : ^^^^^^^^^ ^^^^^^^^^^ ^^^ + + // ^? + const l = opt.label; +>l : ILocalizedString +> : ^^^^^^^^^^^^^^^^ +>opt.label : ILocalizedString +> : ^^^^^^^^^^^^^^^^ +>opt : { label: ILocalizedString; alias?: string; } +> : ^^^^^^^^^ ^^^^^^^^^^ ^^^ +>label : ILocalizedString +> : ^^^^^^^^^^^^^^^^ + + const a = opt.alias ?? opt.label.original; +>a : string +> : ^^^^^^ +>opt.alias ?? opt.label.original : string +> : ^^^^^^ +>opt.alias : string | undefined +> : ^^^^^^^^^^^^^^^^^^ +>opt : { label: ILocalizedString; alias?: string; } +> : ^^^^^^^^^ ^^^^^^^^^^ ^^^ +>alias : string | undefined +> : ^^^^^^^^^^^^^^^^^^ +>opt.label.original : string +> : ^^^^^^ +>opt.label : ILocalizedString +> : ^^^^^^^^^^^^^^^^ +>opt : { label: ILocalizedString; alias?: string; } +> : ^^^^^^^^^ ^^^^^^^^^^ ^^^ +>label : ILocalizedString +> : ^^^^^^^^^^^^^^^^ +>original : string +> : ^^^^^^ +} + + +type PackageDetails = { +>PackageDetails : PackageDetails +> : ^^^^^^^^^^^^^^ + + docsLink?: string; +>docsLink : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + docsLinkWarning?: string; +>docsLinkWarning : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + title?: string; +>title : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + description?: string; +>description : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + repo?: string; +>repo : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + license?: string; +>license : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + +}; + +type PackageDetailsSuccess = PackageDetails & { +>PackageDetailsSuccess : PackageDetailsSuccess +> : ^^^^^^^^^^^^^^^^^^^^^ + + docsLink: string; +>docsLink : string +> : ^^^^^^ + +}; + +interface FilePathAndName { + path: string; +>path : string +> : ^^^^^^ + + name: string; +>name : string +> : ^^^^^^ +} + +interface PackageFilePathAndName extends FilePathAndName { + packageRegistry: string; // e.g. npm, pypi +>packageRegistry : string +> : ^^^^^^ +} + +type ParsedPackageInfo = { +>ParsedPackageInfo : ParsedPackageInfo +> : ^^^^^^^^^^^^^^^^^ + + name: string; +>name : string +> : ^^^^^^ + + packageFile: PackageFilePathAndName; +>packageFile : PackageFilePathAndName +> : ^^^^^^^^^^^^^^^^^^^^^^ + + language: string; +>language : string +> : ^^^^^^ + + version: string; +>version : string +> : ^^^^^^ + +}; + + +type PackageDocsResult = { +>PackageDocsResult : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ + + packageInfo: ParsedPackageInfo; +>packageInfo : ParsedPackageInfo +> : ^^^^^^^^^^^^^^^^^ + +} & ({ + error: string; +>error : string +> : ^^^^^^ + + details?: never; +>details : undefined +> : ^^^^^^^^^ + +} | { + details: PackageDetailsSuccess; +>details : PackageDetailsSuccess +> : ^^^^^^^^^^^^^^^^^^^^^ + + error?: never; +>error : undefined +> : ^^^^^^^^^ + +}) + +declare const filtered: PackageDocsResult[]; +>filtered : PackageDocsResult[] +> : ^^^^^^^^^^^^^^^^^^^ + +filtered.sort((a, b) => { +>filtered.sort((a, b) => { const rank = (result: PackageDocsResult) => { result.details if (result.error) { return 2; } else if (result.details?.docsLinkWarning) { return 1; } else { return 0; } }; return rank(a) - rank(b);}) : PackageDocsResult[] +> : ^^^^^^^^^^^^^^^^^^^ +>filtered.sort : (compareFn?: ((a: PackageDocsResult, b: PackageDocsResult) => number) | undefined) => PackageDocsResult[] +> : ^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>filtered : PackageDocsResult[] +> : ^^^^^^^^^^^^^^^^^^^ +>sort : (compareFn?: ((a: PackageDocsResult, b: PackageDocsResult) => number) | undefined) => PackageDocsResult[] +> : ^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(a, b) => { const rank = (result: PackageDocsResult) => { result.details if (result.error) { return 2; } else if (result.details?.docsLinkWarning) { return 1; } else { return 0; } }; return rank(a) - rank(b);} : (a: PackageDocsResult, b: PackageDocsResult) => number +> : ^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>a : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ +>b : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ + + const rank = (result: PackageDocsResult) => { +>rank : (result: PackageDocsResult) => 0 | 1 | 2 +> : ^ ^^ ^^^^^^^^^^^^^^ +>(result: PackageDocsResult) => { result.details if (result.error) { return 2; } else if (result.details?.docsLinkWarning) { return 1; } else { return 0; } } : (result: PackageDocsResult) => 0 | 1 | 2 +> : ^ ^^ ^^^^^^^^^^^^^^ +>result : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ + + result.details +>result.details : PackageDetailsSuccess | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>result : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ +>details : PackageDetailsSuccess | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + if (result.error) { +>result.error : string | undefined +> : ^^^^^^^^^^^^^^^^^^ +>result : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ +>error : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + return 2; +>2 : 2 +> : ^ + + } else if (result.details?.docsLinkWarning) { +>result.details?.docsLinkWarning : string | undefined +> : ^^^^^^^^^^^^^^^^^^ +>result.details : PackageDetailsSuccess | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>result : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ +>details : PackageDetailsSuccess | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>docsLinkWarning : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + return 1; +>1 : 1 +> : ^ + + } else { + return 0; +>0 : 0 +> : ^ + } + }; + return rank(a) - rank(b); +>rank(a) - rank(b) : number +> : ^^^^^^ +>rank(a) : 0 | 2 | 1 +> : ^^^^^^^^^ +>rank : (result: PackageDocsResult) => 0 | 1 | 2 +> : ^ ^^ ^^^^^^^^^^^^^^ +>a : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ +>rank(b) : 0 | 2 | 1 +> : ^^^^^^^^^ +>rank : (result: PackageDocsResult) => 0 | 1 | 2 +> : ^ ^^ ^^^^^^^^^^^^^^ +>b : PackageDocsResult +> : ^^^^^^^^^^^^^^^^^ + +}); diff --git a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts index 15e9551db162b..038802f0ae94e 100644 --- a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts +++ b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts @@ -1,4 +1,8 @@ // @strict: true +// @exactOptionalPropertyTypes: true +// @noEmit: true + +export {} interface State { state: Type; @@ -73,4 +77,89 @@ if(typeof obj2.prop === "string") { obj2.other.repeat(3); } else { Math.exp(obj2.other); -} \ No newline at end of file +} + + +interface ILocalizedString { + original: string; + value: string; +} + +type Opt = ({ + label: ILocalizedString; + alias?: string; +} | { + label: string; + alias: string; +}) + +declare const opt: Opt + +if (typeof opt.label === 'string') { + opt + // ^? + const l = opt.label; + const a = opt.alias ?? opt.label; +} else { + opt + // ^? + const l = opt.label; + const a = opt.alias ?? opt.label.original; +} + + +type PackageDetails = { + docsLink?: string; + docsLinkWarning?: string; + title?: string; + description?: string; + repo?: string; + license?: string; +}; + +type PackageDetailsSuccess = PackageDetails & { + docsLink: string; +}; + +interface FilePathAndName { + path: string; + name: string; +} + +interface PackageFilePathAndName extends FilePathAndName { + packageRegistry: string; // e.g. npm, pypi +} + +type ParsedPackageInfo = { + name: string; + packageFile: PackageFilePathAndName; + language: string; + version: string; +}; + + +type PackageDocsResult = { + packageInfo: ParsedPackageInfo; +} & ({ + error: string; + details?: never; +} | { + details: PackageDetailsSuccess; + error?: never; +}) + +declare const filtered: PackageDocsResult[]; + +filtered.sort((a, b) => { + const rank = (result: PackageDocsResult) => { + result.details + if (result.error) { + return 2; + } else if (result.details?.docsLinkWarning) { + return 1; + } else { + return 0; + } + }; + return rank(a) - rank(b); +}); \ No newline at end of file From 602ca723a66dddbda03c8f78da3566872b277f61 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Tue, 10 Dec 2024 12:26:59 +0100 Subject: [PATCH 20/27] add test case --- ...ionOfObjectsByPrimitiveProperty.errors.txt | 65 +----- ...narrowUnionOfObjectsByPrimitiveProperty.js | 160 ++++++++++++++ ...wUnionOfObjectsByPrimitiveProperty.symbols | 157 +------------ ...rowUnionOfObjectsByPrimitiveProperty.types | 206 ------------------ ...narrowUnionOfObjectsByPrimitiveProperty.ts | 65 +----- 5 files changed, 167 insertions(+), 486 deletions(-) create mode 100644 tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt index e1fecffb6c46c..d6ccf794a6e2a 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt @@ -11,7 +11,7 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(70,1): error TS2322: Type '{ prop: st Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'. narrowUnionOfObjectsByPrimitiveProperty.ts(73,14): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. -narrowUnionOfObjectsByPrimitiveProperty.ts(98,32): error TS2339: Property 'label' does not exist on type 'never'. +narrowUnionOfObjectsByPrimitiveProperty.ts(96,32): error TS2339: Property 'label' does not exist on type 'never'. ==== narrowUnionOfObjectsByPrimitiveProperty.ts (6 errors) ==== @@ -127,72 +127,11 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(98,32): error TS2339: Property 'label declare const opt: Opt if (typeof opt.label === 'string') { - opt - // ^? const l = opt.label; const a = opt.alias ?? opt.label; ~~~~~ !!! error TS2339: Property 'label' does not exist on type 'never'. } else { - opt - // ^? const l = opt.label; const a = opt.alias ?? opt.label.original; - } - - - type PackageDetails = { - docsLink?: string; - docsLinkWarning?: string; - title?: string; - description?: string; - repo?: string; - license?: string; - }; - - type PackageDetailsSuccess = PackageDetails & { - docsLink: string; - }; - - interface FilePathAndName { - path: string; - name: string; - } - - interface PackageFilePathAndName extends FilePathAndName { - packageRegistry: string; // e.g. npm, pypi - } - - type ParsedPackageInfo = { - name: string; - packageFile: PackageFilePathAndName; - language: string; - version: string; - }; - - - type PackageDocsResult = { - packageInfo: ParsedPackageInfo; - } & ({ - error: string; - details?: never; - } | { - details: PackageDetailsSuccess; - error?: never; - }) - - declare const filtered: PackageDocsResult[]; - - filtered.sort((a, b) => { - const rank = (result: PackageDocsResult) => { - result.details - if (result.error) { - return 2; - } else if (result.details?.docsLinkWarning) { - return 1; - } else { - return 0; - } - }; - return rank(a) - rank(b); - }); \ No newline at end of file + } \ No newline at end of file diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js new file mode 100644 index 0000000000000..e1229e3ffb5d2 --- /dev/null +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js @@ -0,0 +1,160 @@ +//// [tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts] //// + +//// [narrowUnionOfObjectsByPrimitiveProperty.ts] +export {} + +interface State { + state: Type; +} + +interface UserName { + first: string; + last?: string; +} + +const nameState = {} as { + value: string; + state: State; +} | { + value: UserName; + state: State; +} + +if (typeof nameState.value === "string") { + nameState.state satisfies State; +} else { + nameState.state satisfies State; +} + + +declare const arr: [string, number] | [number, string]; +if (typeof arr[0] === "string") { + arr[1] satisfies number; +} else { + arr[1] satisfies string; +} + + +function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { + if (typeof param.a === "string") { + return param.a.repeat(3); + } + if (typeof param.a === "number") { + return Math.exp(param.a); + } + throw new Error() +} + +aStringOrANumber({ a: "string" }) +aStringOrANumber({ a: 42 }) + + +// The following two tests ensure that the discriminativeness of property 'prop' +// is treated differently in assignability and narrowing, and that the discriminativeness is properly cached. +declare let obj: { prop: string, other: string } | { prop: number, other: number } + +// Here, we first perform narrowing, but the subsequent assignability should not be affected. +// We expect an error there because of an incorrect value assigned to 'prop'. +// See contextualTypeWithUnionTypeObjectLiteral.ts +if(typeof obj.prop === "string") { + obj.other.repeat(3); +} else { + Math.exp(obj.other); +} + +obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } + + +declare let obj2: { prop: string, other: string } | { prop: number, other: number } + +// Here, we first assign a value to 'obj2' and then perform narrowing. +// We expect an error here because of an incorrect value assigned to 'prop', like above, +// but the subsequent narrowing should not be affected by the assignability. +obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as never } + +if(typeof obj2.prop === "string") { + obj2.other.repeat(3); +} else { + Math.exp(obj2.other); +} + + +interface ILocalizedString { + original: string; + value: string; +} + +type Opt = ({ + label: ILocalizedString; + alias?: string; +} | { + label: string; + alias: string; +}) + +declare const opt: Opt + +if (typeof opt.label === 'string') { + const l = opt.label; + const a = opt.alias ?? opt.label; +} else { + const l = opt.label; + const a = opt.alias ?? opt.label.original; +} + +//// [narrowUnionOfObjectsByPrimitiveProperty.js] +"use strict"; +var _a, _b; +Object.defineProperty(exports, "__esModule", { value: true }); +var nameState = {}; +if (typeof nameState.value === "string") { + nameState.state; +} +else { + nameState.state; +} +if (typeof arr[0] === "string") { + arr[1]; +} +else { + arr[1]; +} +function aStringOrANumber(param) { + if (typeof param.a === "string") { + return param.a.repeat(3); + } + if (typeof param.a === "number") { + return Math.exp(param.a); + } + throw new Error(); +} +aStringOrANumber({ a: "string" }); +aStringOrANumber({ a: 42 }); +// Here, we first perform narrowing, but the subsequent assignability should not be affected. +// We expect an error there because of an incorrect value assigned to 'prop'. +// See contextualTypeWithUnionTypeObjectLiteral.ts +if (typeof obj.prop === "string") { + obj.other.repeat(3); +} +else { + Math.exp(obj.other); +} +obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" }; +// Here, we first assign a value to 'obj2' and then perform narrowing. +// We expect an error here because of an incorrect value assigned to 'prop', like above, +// but the subsequent narrowing should not be affected by the assignability. +obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" }; +if (typeof obj2.prop === "string") { + obj2.other.repeat(3); +} +else { + Math.exp(obj2.other); +} +if (typeof opt.label === 'string') { + var l = opt.label; + var a = (_a = opt.alias) !== null && _a !== void 0 ? _a : opt.label; +} +else { + var l = opt.label; + var a = (_b = opt.alias) !== null && _b !== void 0 ? _b : opt.label.original; +} diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols index 0cf87d505e958..b093b7d4aaaae 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols @@ -248,36 +248,28 @@ if (typeof opt.label === 'string') { >opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) >label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 86, 5)) - opt ->opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) - - // ^? const l = opt.label; ->l : Symbol(l, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 96, 9)) +>l : Symbol(l, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 94, 9)) >opt.label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 86, 5)) >opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) >label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 86, 5)) const a = opt.alias ?? opt.label; ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 97, 9)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 95, 9)) >opt.alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 87, 18)) >opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) >alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 87, 18)) >opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) } else { - opt ->opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) - - // ^? const l = opt.label; ->l : Symbol(l, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 101, 9)) +>l : Symbol(l, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 97, 9)) >opt.label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13)) >opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) >label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13)) const a = opt.alias ?? opt.label.original; ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 102, 9)) +>a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 98, 9)) >opt.alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 84, 28)) >opt : Symbol(opt, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 91, 13)) >alias : Symbol(alias, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 84, 28)) @@ -287,144 +279,3 @@ if (typeof opt.label === 'string') { >label : Symbol(label, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 83, 13)) >original : Symbol(ILocalizedString.original, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 78, 28)) } - - -type PackageDetails = { ->PackageDetails : Symbol(PackageDetails, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 103, 1)) - - docsLink?: string; ->docsLink : Symbol(docsLink, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 106, 23)) - - docsLinkWarning?: string; ->docsLinkWarning : Symbol(docsLinkWarning, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 107, 20)) - - title?: string; ->title : Symbol(title, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 108, 27)) - - description?: string; ->description : Symbol(description, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 109, 17)) - - repo?: string; ->repo : Symbol(repo, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 110, 23)) - - license?: string; ->license : Symbol(license, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 111, 16)) - -}; - -type PackageDetailsSuccess = PackageDetails & { ->PackageDetailsSuccess : Symbol(PackageDetailsSuccess, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 113, 2)) ->PackageDetails : Symbol(PackageDetails, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 103, 1)) - - docsLink: string; ->docsLink : Symbol(docsLink, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 115, 47)) - -}; - -interface FilePathAndName { ->FilePathAndName : Symbol(FilePathAndName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 117, 2)) - - path: string; ->path : Symbol(FilePathAndName.path, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 119, 27)) - - name: string; ->name : Symbol(FilePathAndName.name, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 120, 15)) -} - -interface PackageFilePathAndName extends FilePathAndName { ->PackageFilePathAndName : Symbol(PackageFilePathAndName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 122, 1)) ->FilePathAndName : Symbol(FilePathAndName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 117, 2)) - - packageRegistry: string; // e.g. npm, pypi ->packageRegistry : Symbol(PackageFilePathAndName.packageRegistry, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 124, 58)) -} - -type ParsedPackageInfo = { ->ParsedPackageInfo : Symbol(ParsedPackageInfo, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 126, 1)) - - name: string; ->name : Symbol(name, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 128, 26)) - - packageFile: PackageFilePathAndName; ->packageFile : Symbol(packageFile, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 129, 15)) ->PackageFilePathAndName : Symbol(PackageFilePathAndName, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 122, 1)) - - language: string; ->language : Symbol(language, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 130, 38)) - - version: string; ->version : Symbol(version, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 131, 19)) - -}; - - -type PackageDocsResult = { ->PackageDocsResult : Symbol(PackageDocsResult, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 133, 2)) - - packageInfo: ParsedPackageInfo; ->packageInfo : Symbol(packageInfo, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 136, 26)) ->ParsedPackageInfo : Symbol(ParsedPackageInfo, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 126, 1)) - -} & ({ - error: string; ->error : Symbol(error, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 138, 6)) - - details?: never; ->details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18)) - -} | { - details: PackageDetailsSuccess; ->details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) ->PackageDetailsSuccess : Symbol(PackageDetailsSuccess, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 113, 2)) - - error?: never; ->error : Symbol(error, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 142, 35)) - -}) - -declare const filtered: PackageDocsResult[]; ->filtered : Symbol(filtered, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 146, 13)) ->PackageDocsResult : Symbol(PackageDocsResult, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 133, 2)) - -filtered.sort((a, b) => { ->filtered.sort : Symbol(Array.sort, Decl(lib.es5.d.ts, --, --)) ->filtered : Symbol(filtered, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 146, 13)) ->sort : Symbol(Array.sort, Decl(lib.es5.d.ts, --, --)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 148, 15)) ->b : Symbol(b, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 148, 17)) - - const rank = (result: PackageDocsResult) => { ->rank : Symbol(rank, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 7)) ->result : Symbol(result, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 16)) ->PackageDocsResult : Symbol(PackageDocsResult, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 133, 2)) - - result.details ->result.details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) ->result : Symbol(result, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 16)) ->details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) - - if (result.error) { ->result.error : Symbol(error, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 138, 6), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 142, 35)) ->result : Symbol(result, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 16)) ->error : Symbol(error, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 138, 6), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 142, 35)) - - return 2; - } else if (result.details?.docsLinkWarning) { ->result.details?.docsLinkWarning : Symbol(docsLinkWarning, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 107, 20)) ->result.details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) ->result : Symbol(result, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 16)) ->details : Symbol(details, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 139, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 141, 5)) ->docsLinkWarning : Symbol(docsLinkWarning, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 107, 20)) - - return 1; - } else { - return 0; - } - }; - return rank(a) - rank(b); ->rank : Symbol(rank, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 7)) ->a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 148, 15)) ->rank : Symbol(rank, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 149, 7)) ->b : Symbol(b, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 148, 17)) - -}); diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types index b98869dc7eafe..a3326583fc5f6 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types @@ -478,11 +478,6 @@ if (typeof opt.label === 'string') { >'string' : "string" > : ^^^^^^^^ - opt ->opt : { label: string; alias: string; } -> : ^^^^^^^^^ ^^^^^^^^^ ^^^ - - // ^? const l = opt.label; >l : string > : ^^^^^^ @@ -512,11 +507,6 @@ if (typeof opt.label === 'string') { > : ^^^ } else { - opt ->opt : { label: ILocalizedString; alias?: string; } -> : ^^^^^^^^^ ^^^^^^^^^^ ^^^ - - // ^? const l = opt.label; >l : ILocalizedString > : ^^^^^^^^^^^^^^^^ @@ -549,199 +539,3 @@ if (typeof opt.label === 'string') { >original : string > : ^^^^^^ } - - -type PackageDetails = { ->PackageDetails : PackageDetails -> : ^^^^^^^^^^^^^^ - - docsLink?: string; ->docsLink : string | undefined -> : ^^^^^^^^^^^^^^^^^^ - - docsLinkWarning?: string; ->docsLinkWarning : string | undefined -> : ^^^^^^^^^^^^^^^^^^ - - title?: string; ->title : string | undefined -> : ^^^^^^^^^^^^^^^^^^ - - description?: string; ->description : string | undefined -> : ^^^^^^^^^^^^^^^^^^ - - repo?: string; ->repo : string | undefined -> : ^^^^^^^^^^^^^^^^^^ - - license?: string; ->license : string | undefined -> : ^^^^^^^^^^^^^^^^^^ - -}; - -type PackageDetailsSuccess = PackageDetails & { ->PackageDetailsSuccess : PackageDetailsSuccess -> : ^^^^^^^^^^^^^^^^^^^^^ - - docsLink: string; ->docsLink : string -> : ^^^^^^ - -}; - -interface FilePathAndName { - path: string; ->path : string -> : ^^^^^^ - - name: string; ->name : string -> : ^^^^^^ -} - -interface PackageFilePathAndName extends FilePathAndName { - packageRegistry: string; // e.g. npm, pypi ->packageRegistry : string -> : ^^^^^^ -} - -type ParsedPackageInfo = { ->ParsedPackageInfo : ParsedPackageInfo -> : ^^^^^^^^^^^^^^^^^ - - name: string; ->name : string -> : ^^^^^^ - - packageFile: PackageFilePathAndName; ->packageFile : PackageFilePathAndName -> : ^^^^^^^^^^^^^^^^^^^^^^ - - language: string; ->language : string -> : ^^^^^^ - - version: string; ->version : string -> : ^^^^^^ - -}; - - -type PackageDocsResult = { ->PackageDocsResult : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ - - packageInfo: ParsedPackageInfo; ->packageInfo : ParsedPackageInfo -> : ^^^^^^^^^^^^^^^^^ - -} & ({ - error: string; ->error : string -> : ^^^^^^ - - details?: never; ->details : undefined -> : ^^^^^^^^^ - -} | { - details: PackageDetailsSuccess; ->details : PackageDetailsSuccess -> : ^^^^^^^^^^^^^^^^^^^^^ - - error?: never; ->error : undefined -> : ^^^^^^^^^ - -}) - -declare const filtered: PackageDocsResult[]; ->filtered : PackageDocsResult[] -> : ^^^^^^^^^^^^^^^^^^^ - -filtered.sort((a, b) => { ->filtered.sort((a, b) => { const rank = (result: PackageDocsResult) => { result.details if (result.error) { return 2; } else if (result.details?.docsLinkWarning) { return 1; } else { return 0; } }; return rank(a) - rank(b);}) : PackageDocsResult[] -> : ^^^^^^^^^^^^^^^^^^^ ->filtered.sort : (compareFn?: ((a: PackageDocsResult, b: PackageDocsResult) => number) | undefined) => PackageDocsResult[] -> : ^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->filtered : PackageDocsResult[] -> : ^^^^^^^^^^^^^^^^^^^ ->sort : (compareFn?: ((a: PackageDocsResult, b: PackageDocsResult) => number) | undefined) => PackageDocsResult[] -> : ^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->(a, b) => { const rank = (result: PackageDocsResult) => { result.details if (result.error) { return 2; } else if (result.details?.docsLinkWarning) { return 1; } else { return 0; } }; return rank(a) - rank(b);} : (a: PackageDocsResult, b: PackageDocsResult) => number -> : ^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->a : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ ->b : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ - - const rank = (result: PackageDocsResult) => { ->rank : (result: PackageDocsResult) => 0 | 1 | 2 -> : ^ ^^ ^^^^^^^^^^^^^^ ->(result: PackageDocsResult) => { result.details if (result.error) { return 2; } else if (result.details?.docsLinkWarning) { return 1; } else { return 0; } } : (result: PackageDocsResult) => 0 | 1 | 2 -> : ^ ^^ ^^^^^^^^^^^^^^ ->result : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ - - result.details ->result.details : PackageDetailsSuccess | undefined -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->result : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ ->details : PackageDetailsSuccess | undefined -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - if (result.error) { ->result.error : string | undefined -> : ^^^^^^^^^^^^^^^^^^ ->result : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ ->error : string | undefined -> : ^^^^^^^^^^^^^^^^^^ - - return 2; ->2 : 2 -> : ^ - - } else if (result.details?.docsLinkWarning) { ->result.details?.docsLinkWarning : string | undefined -> : ^^^^^^^^^^^^^^^^^^ ->result.details : PackageDetailsSuccess | undefined -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->result : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ ->details : PackageDetailsSuccess | undefined -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->docsLinkWarning : string | undefined -> : ^^^^^^^^^^^^^^^^^^ - - return 1; ->1 : 1 -> : ^ - - } else { - return 0; ->0 : 0 -> : ^ - } - }; - return rank(a) - rank(b); ->rank(a) - rank(b) : number -> : ^^^^^^ ->rank(a) : 0 | 2 | 1 -> : ^^^^^^^^^ ->rank : (result: PackageDocsResult) => 0 | 1 | 2 -> : ^ ^^ ^^^^^^^^^^^^^^ ->a : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ ->rank(b) : 0 | 2 | 1 -> : ^^^^^^^^^ ->rank : (result: PackageDocsResult) => 0 | 1 | 2 -> : ^ ^^ ^^^^^^^^^^^^^^ ->b : PackageDocsResult -> : ^^^^^^^^^^^^^^^^^ - -}); diff --git a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts index 038802f0ae94e..699530a281118 100644 --- a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts +++ b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts @@ -1,6 +1,4 @@ // @strict: true -// @exactOptionalPropertyTypes: true -// @noEmit: true export {} @@ -96,70 +94,9 @@ type Opt = ({ declare const opt: Opt if (typeof opt.label === 'string') { - opt - // ^? const l = opt.label; const a = opt.alias ?? opt.label; } else { - opt - // ^? const l = opt.label; const a = opt.alias ?? opt.label.original; -} - - -type PackageDetails = { - docsLink?: string; - docsLinkWarning?: string; - title?: string; - description?: string; - repo?: string; - license?: string; -}; - -type PackageDetailsSuccess = PackageDetails & { - docsLink: string; -}; - -interface FilePathAndName { - path: string; - name: string; -} - -interface PackageFilePathAndName extends FilePathAndName { - packageRegistry: string; // e.g. npm, pypi -} - -type ParsedPackageInfo = { - name: string; - packageFile: PackageFilePathAndName; - language: string; - version: string; -}; - - -type PackageDocsResult = { - packageInfo: ParsedPackageInfo; -} & ({ - error: string; - details?: never; -} | { - details: PackageDetailsSuccess; - error?: never; -}) - -declare const filtered: PackageDocsResult[]; - -filtered.sort((a, b) => { - const rank = (result: PackageDocsResult) => { - result.details - if (result.error) { - return 2; - } else if (result.details?.docsLinkWarning) { - return 1; - } else { - return 0; - } - }; - return rank(a) - rank(b); -}); \ No newline at end of file +} \ No newline at end of file From ae873d42d44f324153992072a70c672696b415d9 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Fri, 13 Dec 2024 21:31:27 +0100 Subject: [PATCH 21/27] remove unwanted target errors from test --- ...ionOfObjectsByPrimitiveProperty.errors.txt | 13 +----- ...narrowUnionOfObjectsByPrimitiveProperty.js | 16 ++++--- ...wUnionOfObjectsByPrimitiveProperty.symbols | 20 +++++---- ...rowUnionOfObjectsByPrimitiveProperty.types | 42 +++++++++---------- ...narrowUnionOfObjectsByPrimitiveProperty.ts | 4 +- 5 files changed, 46 insertions(+), 49 deletions(-) diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt index d6ccf794a6e2a..f063d133d32c9 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.errors.txt @@ -1,5 +1,3 @@ -narrowUnionOfObjectsByPrimitiveProperty.ts(37,20): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. -narrowUnionOfObjectsByPrimitiveProperty.ts(57,13): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. narrowUnionOfObjectsByPrimitiveProperty.ts(62,1): error TS2322: Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: string; other: string; } | { prop: number; other: number; }'. Type '{ prop: string | number; other: never; }' is not assignable to type '{ prop: number; other: number; }'. Types of property 'prop' are incompatible. @@ -10,11 +8,10 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(70,1): error TS2322: Type '{ prop: st Types of property 'prop' are incompatible. Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'. -narrowUnionOfObjectsByPrimitiveProperty.ts(73,14): error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. narrowUnionOfObjectsByPrimitiveProperty.ts(96,32): error TS2339: Property 'label' does not exist on type 'never'. -==== narrowUnionOfObjectsByPrimitiveProperty.ts (6 errors) ==== +==== narrowUnionOfObjectsByPrimitiveProperty.ts (3 errors) ==== export {} interface State { @@ -35,7 +32,7 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(96,32): error TS2339: Property 'label } if (typeof nameState.value === "string") { - nameState.state satisfies State; + nameState.state satisfies State; } else { nameState.state satisfies State; } @@ -52,8 +49,6 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(96,32): error TS2339: Property 'label function aStringOrANumber(param: T): T extends { a: string } ? string : T extends { a: number } ? number : never { if (typeof param.a === "string") { return param.a.repeat(3); - ~~~~~~ -!!! error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. } if (typeof param.a === "number") { return Math.exp(param.a); @@ -74,8 +69,6 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(96,32): error TS2339: Property 'label // See contextualTypeWithUnionTypeObjectLiteral.ts if(typeof obj.prop === "string") { obj.other.repeat(3); - ~~~~~~ -!!! error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. } else { Math.exp(obj.other); } @@ -104,8 +97,6 @@ narrowUnionOfObjectsByPrimitiveProperty.ts(96,32): error TS2339: Property 'label if(typeof obj2.prop === "string") { obj2.other.repeat(3); - ~~~~~~ -!!! error TS2550: Property 'repeat' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later. } else { Math.exp(obj2.other); } diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js index e1229e3ffb5d2..fcd85847e2fc1 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.js @@ -21,7 +21,7 @@ const nameState = {} as { } if (typeof nameState.value === "string") { - nameState.state satisfies State; + nameState.state satisfies State; } else { nameState.state satisfies State; } @@ -103,10 +103,7 @@ if (typeof opt.label === 'string') { } //// [narrowUnionOfObjectsByPrimitiveProperty.js] -"use strict"; -var _a, _b; -Object.defineProperty(exports, "__esModule", { value: true }); -var nameState = {}; +const nameState = {}; if (typeof nameState.value === "string") { nameState.state; } @@ -151,10 +148,11 @@ else { Math.exp(obj2.other); } if (typeof opt.label === 'string') { - var l = opt.label; - var a = (_a = opt.alias) !== null && _a !== void 0 ? _a : opt.label; + const l = opt.label; + const a = opt.alias ?? opt.label; } else { - var l = opt.label; - var a = (_b = opt.alias) !== null && _b !== void 0 ? _b : opt.label.original; + const l = opt.label; + const a = opt.alias ?? opt.label.original; } +export {}; diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols index b093b7d4aaaae..7e814f9d12a7f 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.symbols @@ -48,7 +48,7 @@ if (typeof nameState.value === "string") { >nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 5)) >value : Symbol(value, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 25), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 14, 5)) - nameState.state satisfies State; + nameState.state satisfies State; >nameState.state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 16)) >nameState : Symbol(nameState, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 11, 5)) >state : Symbol(state, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 12, 16)) @@ -100,9 +100,11 @@ function aStringOrANumber(param: T): T >a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 37), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) return param.a.repeat(3); +>param.a.repeat : Symbol(String.repeat, Decl(lib.es2015.core.d.ts, --, --)) >param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 37)) >param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 67)) >a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 37)) +>repeat : Symbol(String.repeat, Decl(lib.es2015.core.d.ts, --, --)) } if (typeof param.a === "number") { >param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) @@ -111,14 +113,14 @@ function aStringOrANumber(param: T): T return Math.exp(param.a); >Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) >param.a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) >param : Symbol(param, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 67)) >a : Symbol(a, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 34, 53)) } throw new Error() ->Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) } aStringOrANumber({ a: "string" }) @@ -148,14 +150,16 @@ if(typeof obj.prop === "string") { >prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 18), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 52)) obj.other.repeat(3); +>obj.other.repeat : Symbol(String.repeat, Decl(lib.es2015.core.d.ts, --, --)) >obj.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 32)) >obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 11)) >other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 32)) +>repeat : Symbol(String.repeat, Decl(lib.es2015.core.d.ts, --, --)) } else { Math.exp(obj.other); >Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) >obj.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 66)) >obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 11)) @@ -166,7 +170,7 @@ obj = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as neve >obj : Symbol(obj, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 50, 11)) >prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 61, 7)) >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) >other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 61, 52)) @@ -185,7 +189,7 @@ obj2 = { prop: Math.random() > 0.5 ? "whatever" : 42, other: "irrelevant" as nev >obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 11)) >prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 69, 8)) >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) >other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 69, 53)) @@ -195,14 +199,16 @@ if(typeof obj2.prop === "string") { >prop : Symbol(prop, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 19), Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 53)) obj2.other.repeat(3); +>obj2.other.repeat : Symbol(String.repeat, Decl(lib.es2015.core.d.ts, --, --)) >obj2.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 33)) >obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 11)) >other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 33)) +>repeat : Symbol(String.repeat, Decl(lib.es2015.core.d.ts, --, --)) } else { Math.exp(obj2.other); >Math.exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >exp : Symbol(Math.exp, Decl(lib.es5.d.ts, --, --)) >obj2.other : Symbol(other, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 67)) >obj2 : Symbol(obj2, Decl(narrowUnionOfObjectsByPrimitiveProperty.ts, 64, 11)) diff --git a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types index a3326583fc5f6..4ae8bd43aeee5 100644 --- a/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types +++ b/tests/baselines/reference/narrowUnionOfObjectsByPrimitiveProperty.types @@ -59,9 +59,9 @@ if (typeof nameState.value === "string") { >"string" : "string" > : ^^^^^^^^ - nameState.state satisfies State; ->nameState.state satisfies State : State -> : ^^^^^^^^^^^^^ + nameState.state satisfies State; +>nameState.state satisfies State : State +> : ^^^^^^^^^^^^^ >nameState.state : State > : ^^^^^^^^^^^^^ >nameState : { value: string; state: State; } @@ -152,18 +152,18 @@ function aStringOrANumber(param: T): T > : ^^^^^^^^ return param.a.repeat(3); ->param.a.repeat(3) : any -> : ^^^ ->param.a.repeat : any -> : ^^^ +>param.a.repeat(3) : string +> : ^^^^^^ +>param.a.repeat : (count: number) => string +> : ^ ^^ ^^^^^ >param.a : string > : ^^^^^^ >param : { a: string; } > : ^^^^^ ^^^ >a : string > : ^^^^^^ ->repeat : any -> : ^^^ +>repeat : (count: number) => string +> : ^ ^^ ^^^^^ >3 : 3 > : ^ } @@ -261,18 +261,18 @@ if(typeof obj.prop === "string") { > : ^^^^^^^^ obj.other.repeat(3); ->obj.other.repeat(3) : any -> : ^^^ ->obj.other.repeat : any -> : ^^^ +>obj.other.repeat(3) : string +> : ^^^^^^ +>obj.other.repeat : (count: number) => string +> : ^ ^^ ^^^^^ >obj.other : string > : ^^^^^^ >obj : { prop: string; other: string; } > : ^^^^^^^^ ^^^^^^^^^ ^^^ >other : string > : ^^^^^^ ->repeat : any -> : ^^^ +>repeat : (count: number) => string +> : ^ ^^ ^^^^^ >3 : 3 > : ^ @@ -393,18 +393,18 @@ if(typeof obj2.prop === "string") { > : ^^^^^^^^ obj2.other.repeat(3); ->obj2.other.repeat(3) : any -> : ^^^ ->obj2.other.repeat : any -> : ^^^ +>obj2.other.repeat(3) : string +> : ^^^^^^ +>obj2.other.repeat : (count: number) => string +> : ^ ^^ ^^^^^ >obj2.other : string > : ^^^^^^ >obj2 : { prop: string; other: string; } > : ^^^^^^^^ ^^^^^^^^^ ^^^ >other : string > : ^^^^^^ ->repeat : any -> : ^^^ +>repeat : (count: number) => string +> : ^ ^^ ^^^^^ >3 : 3 > : ^ diff --git a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts index 699530a281118..7f1ae32d90199 100644 --- a/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts +++ b/tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts @@ -1,4 +1,6 @@ // @strict: true +// @lib: esnext +// @target: esnext export {} @@ -20,7 +22,7 @@ const nameState = {} as { } if (typeof nameState.value === "string") { - nameState.state satisfies State; + nameState.state satisfies State; } else { nameState.state satisfies State; } From 21feb286f4df7fbb69f951377ffa756d3524eebe Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 16 Dec 2024 21:43:43 +0100 Subject: [PATCH 22/27] apply review comment --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a098f8c835151..bec45a24b1764 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27295,7 +27295,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let isDiscriminant = transientSymbol.links.isDiscriminantProperty.get(considerNonUniformPrimitivePropDiscriminant); - if (typeof isDiscriminant === "undefined") { + if (isDiscriminant === undefined) { isDiscriminant = (((transientSymbol.links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) || !!(considerNonUniformPrimitivePropDiscriminant && (transientSymbol.links.checkFlags & CheckFlags.HasNonUniformType) && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) && !isGenericType(propType); @@ -27312,7 +27312,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined { let result: Symbol[] | undefined; for (const sourceProperty of sourceProperties) { - if (isDiscriminantProperty(target, sourceProperty.escapedName, /*considerNonUniformPrimitivePropDiscriminant*/ false)) { + if (isDiscriminantProperty(target, sourceProperty.escapedName, /*considerNonUniformPrimitivePropDiscriminant*/ true)) { if (result) { result.push(sourceProperty); continue; From d401d2eca1fc141c7f6787b76cb975854c0f322d Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 16 Dec 2024 21:48:33 +0100 Subject: [PATCH 23/27] oops --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bec45a24b1764..8eae23aa5bf26 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27312,7 +27312,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined { let result: Symbol[] | undefined; for (const sourceProperty of sourceProperties) { - if (isDiscriminantProperty(target, sourceProperty.escapedName, /*considerNonUniformPrimitivePropDiscriminant*/ true)) { + if (isDiscriminantProperty(target, sourceProperty.escapedName, /*considerNonUniformPrimitivePropDiscriminant*/ false)) { if (result) { result.push(sourceProperty); continue; From 868d5dc1875fb39f58e09b093132d565938b3478 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 16 Dec 2024 22:39:24 +0100 Subject: [PATCH 24/27] wip --- src/compiler/checker.ts | 5 ++++- src/compiler/types.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8eae23aa5bf26..b5c73b7efcdd4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15050,6 +15050,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isLiteralType(type) || isPatternLiteralType(type)) { checkFlags |= CheckFlags.HasLiteralType; } + if (isUnion && (type.flags & TypeFlags.Primitive)) { + checkFlags |= CheckFlags.HasPrimitiveType; + } if (type.flags & TypeFlags.Never && type !== uniqueLiteralType) { checkFlags |= CheckFlags.HasNeverType; } @@ -27297,7 +27300,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isDiscriminant === undefined) { isDiscriminant = (((transientSymbol.links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant) - || !!(considerNonUniformPrimitivePropDiscriminant && (transientSymbol.links.checkFlags & CheckFlags.HasNonUniformType) && someType(propType, t => !!(t.flags & TypeFlags.Primitive)))) + || !!(considerNonUniformPrimitivePropDiscriminant && (transientSymbol.links.checkFlags & (CheckFlags.HasNonUniformType | CheckFlags.HasPrimitiveType)))) && !isGenericType(propType); transientSymbol.links.isDiscriminantProperty.set(considerNonUniformPrimitivePropDiscriminant, isDiscriminant); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8f29782197a51..21c1195c596e0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6071,6 +6071,7 @@ export const enum CheckFlags { Mapped = 1 << 18, // Property of mapped type StripOptional = 1 << 19, // Strip optionality in mapped property Unresolved = 1 << 20, // Unresolved type alias symbol + HasPrimitiveType = 1 << 21, // Synthetic property with at least one primitive type in constituents Synthetic = SyntheticProperty | SyntheticMethod, Discriminant = HasNonUniformType | HasLiteralType, Partial = ReadPartial | WritePartial, From addeec3ba5a9debaf3767141f5759fc243fe476f Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 16 Dec 2024 23:07:54 +0100 Subject: [PATCH 25/27] use flag appropriately --- src/compiler/checker.ts | 6 ++-- .../reference/destructuringControlFlow.types | 32 +++++++++---------- .../discriminatedUnionTypes2.errors.txt | 5 ++- .../discriminatedUnionTypes2.symbols | 2 -- .../reference/discriminatedUnionTypes2.types | 12 +++---- .../reference/narrowingIntersection.symbols | 8 ++--- .../reference/narrowingIntersection.types | 16 +++++----- 7 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b5c73b7efcdd4..5b6fb3b3d4116 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27287,7 +27287,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // The flag `considerNonUniformPrimitivePropDiscriminant`, when enabled, introduces a new criterion for a property to be considered discriminant: // 1 The property must be non-uniform // 2 The property must include at least one primitive type as a possible type - function isDiscriminantProperty(type: Type | undefined, name: __String, considerNonUniformPrimitivePropDiscriminant: boolean = true) { + function isDiscriminantProperty(type: Type | undefined, name: __String, considerNonUniformPrimitivePropDiscriminant: boolean = false) { if (type && type.flags & TypeFlags.Union) { const prop = getUnionOrIntersectionProperty(type as UnionType, name); if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { @@ -27315,7 +27315,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined { let result: Symbol[] | undefined; for (const sourceProperty of sourceProperties) { - if (isDiscriminantProperty(target, sourceProperty.escapedName, /*considerNonUniformPrimitivePropDiscriminant*/ false)) { + if (isDiscriminantProperty(target, sourceProperty.escapedName)) { if (result) { result.push(sourceProperty); continue; @@ -28874,7 +28874,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const name = getAccessedPropertyName(access); if (name) { const type = declaredType.flags & TypeFlags.Union && isTypeSubsetOf(computedType, declaredType) ? declaredType : computedType; - if (isDiscriminantProperty(type, name)) { + if (isDiscriminantProperty(type, name, /*considerNonUniformPrimitivePropDiscriminant*/ true)) { return access; } } diff --git a/tests/baselines/reference/destructuringControlFlow.types b/tests/baselines/reference/destructuringControlFlow.types index 053f79fdf6504..0490486772720 100644 --- a/tests/baselines/reference/destructuringControlFlow.types +++ b/tests/baselines/reference/destructuringControlFlow.types @@ -104,10 +104,10 @@ function f2(obj: [number, string] | null[]) { > : ^^^^^^^^^^^^^^^^^^^^^^^^^ >0 : 0 > : ^ ->obj[1] : string | null -> : ^^^^^^^^^^^^^ ->obj : [number, string] | null[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>obj[1] : string +> : ^^^^^^ +>obj : [number, string] +> : ^^^^^^^^^^^^^^^^ >1 : 1 > : ^ @@ -116,8 +116,8 @@ function f2(obj: [number, string] | null[]) { > : ^^^^^^ >obj[0] : number > : ^^^^^^ ->obj : [number, string] | null[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>obj : [number, string] +> : ^^^^^^^^^^^^^^^^ >0 : 0 > : ^ @@ -126,8 +126,8 @@ function f2(obj: [number, string] | null[]) { > : ^^^^^^ >obj[1] : string > : ^^^^^^ ->obj : [number, string] | null[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>obj : [number, string] +> : ^^^^^^^^^^^^^^^^ >1 : 1 > : ^ @@ -136,22 +136,22 @@ function f2(obj: [number, string] | null[]) { > : ^^^^^^ >d1 : string > : ^^^^^^ ->obj : [number, string] | null[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>obj : [number, string] +> : ^^^^^^^^^^^^^^^^ ([c0, c1] = obj); ->([c0, c1] = obj) : [number, string] | null[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ->[c0, c1] = obj : [number, string] | null[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>([c0, c1] = obj) : [number, string] +> : ^^^^^^^^^^^^^^^^ +>[c0, c1] = obj : [number, string] +> : ^^^^^^^^^^^^^^^^ >[c0, c1] : [number, string] > : ^^^^^^^^^^^^^^^^ >c0 : number > : ^^^^^^ >c1 : string > : ^^^^^^ ->obj : [number, string] | null[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>obj : [number, string] +> : ^^^^^^^^^^^^^^^^ } } diff --git a/tests/baselines/reference/discriminatedUnionTypes2.errors.txt b/tests/baselines/reference/discriminatedUnionTypes2.errors.txt index be69c9aa43f0a..dfbc8d50a1552 100644 --- a/tests/baselines/reference/discriminatedUnionTypes2.errors.txt +++ b/tests/baselines/reference/discriminatedUnionTypes2.errors.txt @@ -1,9 +1,10 @@ discriminatedUnionTypes2.ts(27,30): error TS2353: Object literal may only specify known properties, and 'c' does not exist in type '{ a: null; b: string; }'. discriminatedUnionTypes2.ts(32,11): error TS2339: Property 'b' does not exist on type '{ a: 0; b: string; } | { a: T; c: number; }'. Property 'b' does not exist on type '{ a: T; c: number; }'. +discriminatedUnionTypes2.ts(132,11): error TS2339: Property 'value' does not exist on type 'never'. -==== discriminatedUnionTypes2.ts (2 errors) ==== +==== discriminatedUnionTypes2.ts (3 errors) ==== function f10(x : { kind: false, a: string } | { kind: true, b: string } | { kind: string, c: string }) { if (x.kind === false) { x.a; @@ -141,6 +142,8 @@ discriminatedUnionTypes2.ts(32,11): error TS2339: Property 'b' does not exist on } else { x.value; // number + ~~~~~ +!!! error TS2339: Property 'value' does not exist on type 'never'. } } diff --git a/tests/baselines/reference/discriminatedUnionTypes2.symbols b/tests/baselines/reference/discriminatedUnionTypes2.symbols index f9245740fa8b0..939f86b625660 100644 --- a/tests/baselines/reference/discriminatedUnionTypes2.symbols +++ b/tests/baselines/reference/discriminatedUnionTypes2.symbols @@ -369,9 +369,7 @@ function foo1(x: RuntimeValue & { type: 'number' }) { } else { x.value; // number ->x.value : Symbol(value, Decl(discriminatedUnionTypes2.ts, 122, 23)) >x : Symbol(x, Decl(discriminatedUnionTypes2.ts, 126, 14)) ->value : Symbol(value, Decl(discriminatedUnionTypes2.ts, 122, 23)) } } diff --git a/tests/baselines/reference/discriminatedUnionTypes2.types b/tests/baselines/reference/discriminatedUnionTypes2.types index f67c4ae4daa10..043108d63256e 100644 --- a/tests/baselines/reference/discriminatedUnionTypes2.types +++ b/tests/baselines/reference/discriminatedUnionTypes2.types @@ -578,12 +578,12 @@ function foo1(x: RuntimeValue & { type: 'number' }) { } else { x.value; // number ->x.value : number -> : ^^^^^^ ->x : { type: "number"; value: number; } & { type: "number"; } -> : ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^ ->value : number -> : ^^^^^^ +>x.value : any +> : ^^^ +>x : never +> : ^^^^^ +>value : any +> : ^^^ } } diff --git a/tests/baselines/reference/narrowingIntersection.symbols b/tests/baselines/reference/narrowingIntersection.symbols index f892b87a56689..8e2711a3b83d6 100644 --- a/tests/baselines/reference/narrowingIntersection.symbols +++ b/tests/baselines/reference/narrowingIntersection.symbols @@ -30,15 +30,15 @@ function test1(result: Disjoint): string { >err : Symbol(err, Decl(narrowingIntersection.ts, 4, 28), Decl(narrowingIntersection.ts, 5, 28)) throw result.err; ->result.err : Symbol(err, Decl(narrowingIntersection.ts, 4, 28), Decl(narrowingIntersection.ts, 5, 28)) +>result.err : Symbol(err, Decl(narrowingIntersection.ts, 5, 28)) >result : Symbol(result, Decl(narrowingIntersection.ts, 7, 15)) ->err : Symbol(err, Decl(narrowingIntersection.ts, 4, 28), Decl(narrowingIntersection.ts, 5, 28)) +>err : Symbol(err, Decl(narrowingIntersection.ts, 5, 28)) } // Error, should OK return result.value; ->result.value : Symbol(value, Decl(narrowingIntersection.ts, 4, 4), Decl(narrowingIntersection.ts, 5, 4)) +>result.value : Symbol(value, Decl(narrowingIntersection.ts, 5, 4)) >result : Symbol(result, Decl(narrowingIntersection.ts, 7, 15)) ->value : Symbol(value, Decl(narrowingIntersection.ts, 4, 4), Decl(narrowingIntersection.ts, 5, 4)) +>value : Symbol(value, Decl(narrowingIntersection.ts, 5, 4)) } type TrivialIntersection = { a: 1 } & { a: 1 }; diff --git a/tests/baselines/reference/narrowingIntersection.types b/tests/baselines/reference/narrowingIntersection.types index 60054b938e19b..a4bd30c389d65 100644 --- a/tests/baselines/reference/narrowingIntersection.types +++ b/tests/baselines/reference/narrowingIntersection.types @@ -43,19 +43,19 @@ function test1(result: Disjoint): string { throw result.err; >result.err : FooAndBaz > : ^^^^^^^^^ ->result : Disjoint -> : ^^^^^^^^ +>result : { readonly value?: never; readonly err: FooAndBaz; } +> : ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^ >err : FooAndBaz > : ^^^^^^^^^ } // Error, should OK return result.value; ->result.value : string -> : ^^^^^^ ->result : Disjoint -> : ^^^^^^^^ ->value : string -> : ^^^^^^ +>result.value : never +> : ^^^^^ +>result : { readonly value?: never; readonly err: FooAndBaz; } +> : ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^ +>value : never +> : ^^^^^ } type TrivialIntersection = { a: 1 } & { a: 1 }; From c84695844a826075437c72afd8c4c32a1cb49602 Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Mon, 16 Dec 2024 23:30:35 +0100 Subject: [PATCH 26/27] fix binder --- src/compiler/binder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 41966df6672e9..62937cb9143b8 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -342,7 +342,7 @@ interface ActiveLabel { export function getModuleInstanceState(node: ModuleDeclaration, visited?: Map): ModuleInstanceState { if (node.body && !node.body.parent) { // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already - setParent(node.body, node); + setParent(node.body as ModuleDeclaration["parent"], node); setParentRecursive(node.body, /*incremental*/ false); } return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated; From 981e7eae7dd8671812f0a70a076026379b43991e Mon Sep 17 00:00:00 2001 From: Andrea Simone Costa Date: Tue, 17 Dec 2024 00:20:43 +0100 Subject: [PATCH 27/27] remove useless check --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5b6fb3b3d4116..6581b9027fe3b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15050,7 +15050,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isLiteralType(type) || isPatternLiteralType(type)) { checkFlags |= CheckFlags.HasLiteralType; } - if (isUnion && (type.flags & TypeFlags.Primitive)) { + if (type.flags & TypeFlags.Primitive) { checkFlags |= CheckFlags.HasPrimitiveType; } if (type.flags & TypeFlags.Never && type !== uniqueLiteralType) {