-
Notifications
You must be signed in to change notification settings - Fork 0
Let primitive types discriminate a union of objects #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 17 commits
7f3d3fb
1bc0a55
48e86a7
dc0866f
8542a99
2b69230
6959a73
a4c22c2
fd99a91
6f6d928
3c9b24b
086bbe4
f46d0dd
8e0cf94
e8741f2
443320a
2e87e81
81b5d4a
6c0d085
5566306
602ca72
ae873d4
21feb28
d401d2e
868d5dc
addeec3
c846958
981e7ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4989,7 +4989,7 @@ declare namespace ts { | |
readonly modifiers?: NodeArray<Modifier>; | ||
readonly equalsGreaterThanToken: EqualsGreaterThanToken; | ||
readonly body: ConciseBody; | ||
readonly name: never; | ||
readonly name?: never; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is basically There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. idk if it is set to undefined somewhere else, I'd say it isn't. Even updateArrowFunction ignores it |
||
} | ||
interface LiteralLikeNode extends Node { | ||
text: string; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
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 (5 errors) ==== | ||
interface State<Type> { | ||
state: Type; | ||
} | ||
|
||
interface UserName { | ||
first: string; | ||
last?: string; | ||
} | ||
|
||
const nameState = {} as { | ||
value: string; | ||
state: State<string>; | ||
} | { | ||
value: UserName; | ||
state: State<UserName>; | ||
} | ||
|
||
if (typeof nameState.value === "string") { | ||
nameState.state satisfies State<string>; | ||
} else { | ||
nameState.state satisfies State<UserName>; | ||
} | ||
|
||
|
||
declare const arr: [string, number] | [number, string]; | ||
if (typeof arr[0] === "string") { | ||
arr[1] satisfies number; | ||
} else { | ||
arr[1] satisfies string; | ||
} | ||
|
||
|
||
function aStringOrANumber<T extends { a: string } | { a: number }>(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 }) | ||
|
||
|
||
// 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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
//// [tests/cases/compiler/narrowUnionOfObjectsByPrimitiveProperty.ts] //// | ||
|
||
//// [narrowUnionOfObjectsByPrimitiveProperty.ts] | ||
interface State<Type> { | ||
state: Type; | ||
} | ||
|
||
interface UserName { | ||
first: string; | ||
last?: string; | ||
} | ||
|
||
const nameState = {} as { | ||
value: string; | ||
state: State<string>; | ||
} | { | ||
value: UserName; | ||
state: State<UserName>; | ||
} | ||
|
||
if (typeof nameState.value === "string") { | ||
nameState.state satisfies State<string>; | ||
} else { | ||
nameState.state satisfies State<UserName>; | ||
} | ||
|
||
|
||
declare const arr: [string, number] | [number, string]; | ||
if (typeof arr[0] === "string") { | ||
arr[1] satisfies number; | ||
} else { | ||
arr[1] satisfies string; | ||
} | ||
|
||
|
||
function aStringOrANumber<T extends { a: string } | { a: number }>(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); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.