From 2be000dd7a2d3787e83b867ee35adfa9186d295d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 1 Feb 2025 17:38:54 +0100 Subject: [PATCH] Always discriminate contextual types by existing discriminant property --- src/compiler/checker.ts | 15 +- ...extuallyTypedByDiscriminableUnion2.symbols | 95 +++++++++++ ...ntextuallyTypedByDiscriminableUnion2.types | 160 ++++++++++++++++++ ...extuallyTypedByDiscriminableUnion3.symbols | 96 +++++++++++ ...ntextuallyTypedByDiscriminableUnion3.types | 132 +++++++++++++++ .../contextuallyTypedJsxAttribute3.symbols | 83 +++++++++ .../contextuallyTypedJsxAttribute3.types | 136 +++++++++++++++ .../contextuallyTypedByDiscriminableUnion2.ts | 41 +++++ .../contextuallyTypedByDiscriminableUnion3.ts | 39 +++++ .../contextuallyTypedJsxAttribute3.tsx | 41 +++++ 10 files changed, 827 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/contextuallyTypedByDiscriminableUnion2.symbols create mode 100644 tests/baselines/reference/contextuallyTypedByDiscriminableUnion2.types create mode 100644 tests/baselines/reference/contextuallyTypedByDiscriminableUnion3.symbols create mode 100644 tests/baselines/reference/contextuallyTypedByDiscriminableUnion3.types create mode 100644 tests/baselines/reference/contextuallyTypedJsxAttribute3.symbols create mode 100644 tests/baselines/reference/contextuallyTypedJsxAttribute3.types create mode 100644 tests/cases/compiler/contextuallyTypedByDiscriminableUnion2.ts create mode 100644 tests/cases/compiler/contextuallyTypedByDiscriminableUnion3.ts create mode 100644 tests/cases/compiler/contextuallyTypedJsxAttribute3.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fcb93c45dc1dd..b39c5f426fea3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32029,16 +32029,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { concatenate( map( filter(node.properties, (p): p is PropertyAssignment | ShorthandPropertyAssignment => { - if (!p.symbol) { - return false; - } - if (p.kind === SyntaxKind.PropertyAssignment) { - return isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName); - } - if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { - return isDiscriminantProperty(contextualType, p.symbol.escapedName); - } - return false; + return !!p.symbol + && (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) + && isDiscriminantProperty(contextualType, p.symbol.escapedName); }), prop => ([() => getContextFreeTypeOfExpression(prop.kind === SyntaxKind.PropertyAssignment ? prop.initializer : prop.name), prop.symbol.escapedName] as const), ), @@ -32063,7 +32056,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { contextualType, concatenate( map( - filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName) && (!p.initializer || isPossiblyDiscriminantValue(p.initializer))), + filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName)), prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => getContextFreeTypeOfExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as const), ), map( diff --git a/tests/baselines/reference/contextuallyTypedByDiscriminableUnion2.symbols b/tests/baselines/reference/contextuallyTypedByDiscriminableUnion2.symbols new file mode 100644 index 0000000000000..58398d9f0401d --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedByDiscriminableUnion2.symbols @@ -0,0 +1,95 @@ +//// [tests/cases/compiler/contextuallyTypedByDiscriminableUnion2.ts] //// + +=== contextuallyTypedByDiscriminableUnion2.ts === +type Props = +>Props : Symbol(Props, Decl(contextuallyTypedByDiscriminableUnion2.ts, 0, 0)) + + | { + parentId: string[]; +>parentId : Symbol(parentId, Decl(contextuallyTypedByDiscriminableUnion2.ts, 1, 5)) + + onChange: (event: { id: string }) => void; +>onChange : Symbol(onChange, Decl(contextuallyTypedByDiscriminableUnion2.ts, 2, 25)) +>event : Symbol(event, Decl(contextuallyTypedByDiscriminableUnion2.ts, 3, 17)) +>id : Symbol(id, Decl(contextuallyTypedByDiscriminableUnion2.ts, 3, 25)) + + onChange2: () => void; +>onChange2 : Symbol(onChange2, Decl(contextuallyTypedByDiscriminableUnion2.ts, 3, 48)) + } + | { + parentId?: never; +>parentId : Symbol(parentId, Decl(contextuallyTypedByDiscriminableUnion2.ts, 6, 5)) + + onChange: (event: { id: number }) => void; +>onChange : Symbol(onChange, Decl(contextuallyTypedByDiscriminableUnion2.ts, 7, 23)) +>event : Symbol(event, Decl(contextuallyTypedByDiscriminableUnion2.ts, 8, 17)) +>id : Symbol(id, Decl(contextuallyTypedByDiscriminableUnion2.ts, 8, 25)) + + }; + +function NonGenericComponent(props: Props) { +>NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedByDiscriminableUnion2.ts, 9, 6)) +>props : Symbol(props, Decl(contextuallyTypedByDiscriminableUnion2.ts, 11, 29)) +>Props : Symbol(Props, Decl(contextuallyTypedByDiscriminableUnion2.ts, 0, 0)) + + return null; +} + +NonGenericComponent({ +>NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedByDiscriminableUnion2.ts, 9, 6)) + + onChange: (e) => {}, +>onChange : Symbol(onChange, Decl(contextuallyTypedByDiscriminableUnion2.ts, 15, 21)) +>e : Symbol(e, Decl(contextuallyTypedByDiscriminableUnion2.ts, 16, 13)) + +}); + +const parentId: string[] = []; +>parentId : Symbol(parentId, Decl(contextuallyTypedByDiscriminableUnion2.ts, 19, 5)) + +NonGenericComponent({ +>NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedByDiscriminableUnion2.ts, 9, 6)) + + parentId, +>parentId : Symbol(parentId, Decl(contextuallyTypedByDiscriminableUnion2.ts, 21, 21)) + + onChange: (e) => {}, +>onChange : Symbol(onChange, Decl(contextuallyTypedByDiscriminableUnion2.ts, 22, 11)) +>e : Symbol(e, Decl(contextuallyTypedByDiscriminableUnion2.ts, 23, 13)) + + onChange2: () => {}, +>onChange2 : Symbol(onChange2, Decl(contextuallyTypedByDiscriminableUnion2.ts, 23, 22)) + +}); + +NonGenericComponent({ +>NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedByDiscriminableUnion2.ts, 9, 6)) + + parentId: parentId, +>parentId : Symbol(parentId, Decl(contextuallyTypedByDiscriminableUnion2.ts, 27, 21)) +>parentId : Symbol(parentId, Decl(contextuallyTypedByDiscriminableUnion2.ts, 19, 5)) + + onChange: (e) => {}, +>onChange : Symbol(onChange, Decl(contextuallyTypedByDiscriminableUnion2.ts, 28, 21)) +>e : Symbol(e, Decl(contextuallyTypedByDiscriminableUnion2.ts, 29, 13)) + + onChange2: () => {}, +>onChange2 : Symbol(onChange2, Decl(contextuallyTypedByDiscriminableUnion2.ts, 29, 22)) + +}); + +NonGenericComponent({ +>NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedByDiscriminableUnion2.ts, 9, 6)) + + parentId: [], +>parentId : Symbol(parentId, Decl(contextuallyTypedByDiscriminableUnion2.ts, 33, 21)) + + onChange: (e) => {}, +>onChange : Symbol(onChange, Decl(contextuallyTypedByDiscriminableUnion2.ts, 34, 15)) +>e : Symbol(e, Decl(contextuallyTypedByDiscriminableUnion2.ts, 35, 13)) + + onChange2: () => {}, +>onChange2 : Symbol(onChange2, Decl(contextuallyTypedByDiscriminableUnion2.ts, 35, 22)) + +}); + diff --git a/tests/baselines/reference/contextuallyTypedByDiscriminableUnion2.types b/tests/baselines/reference/contextuallyTypedByDiscriminableUnion2.types new file mode 100644 index 0000000000000..7dd02190deb1d --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedByDiscriminableUnion2.types @@ -0,0 +1,160 @@ +//// [tests/cases/compiler/contextuallyTypedByDiscriminableUnion2.ts] //// + +=== contextuallyTypedByDiscriminableUnion2.ts === +type Props = +>Props : Props +> : ^^^^^ + + | { + parentId: string[]; +>parentId : string[] +> : ^^^^^^^^ + + onChange: (event: { id: string }) => void; +>onChange : (event: { id: string; }) => void +> : ^ ^^ ^^^^^ +>event : { id: string; } +> : ^^^^^^ ^^^ +>id : string +> : ^^^^^^ + + onChange2: () => void; +>onChange2 : () => void +> : ^^^^^^ + } + | { + parentId?: never; +>parentId : undefined +> : ^^^^^^^^^ + + onChange: (event: { id: number }) => void; +>onChange : (event: { id: number; }) => void +> : ^ ^^ ^^^^^ +>event : { id: number; } +> : ^^^^^^ ^^^ +>id : number +> : ^^^^^^ + + }; + +function NonGenericComponent(props: Props) { +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ +>props : Props +> : ^^^^^ + + return null; +} + +NonGenericComponent({ +>NonGenericComponent({ onChange: (e) => {},}) : null +> : ^^^^ +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ +>{ onChange: (e) => {},} : { onChange: (e: { id: number; }) => void; } +> : ^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^ + + onChange: (e) => {}, +>onChange : (e: { id: number; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>(e) => {} : (e: { id: number; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>e : { id: number; } +> : ^^^^^^ ^^^ + +}); + +const parentId: string[] = []; +>parentId : string[] +> : ^^^^^^^^ +>[] : never[] +> : ^^^^^^^ + +NonGenericComponent({ +>NonGenericComponent({ parentId, onChange: (e) => {}, onChange2: () => {},}) : null +> : ^^^^ +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ +>{ parentId, onChange: (e) => {}, onChange2: () => {},} : { parentId: string[]; onChange: (e: { id: string; }) => void; onChange2: () => void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + parentId, +>parentId : string[] +> : ^^^^^^^^ + + onChange: (e) => {}, +>onChange : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>(e) => {} : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>e : { id: string; } +> : ^^^^^^ ^^^ + + onChange2: () => {}, +>onChange2 : () => void +> : ^^^^^^^^^^ +>() => {} : () => void +> : ^^^^^^^^^^ + +}); + +NonGenericComponent({ +>NonGenericComponent({ parentId: parentId, onChange: (e) => {}, onChange2: () => {},}) : null +> : ^^^^ +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ +>{ parentId: parentId, onChange: (e) => {}, onChange2: () => {},} : { parentId: string[]; onChange: (e: { id: string; }) => void; onChange2: () => void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + parentId: parentId, +>parentId : string[] +> : ^^^^^^^^ +>parentId : string[] +> : ^^^^^^^^ + + onChange: (e) => {}, +>onChange : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>(e) => {} : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>e : { id: string; } +> : ^^^^^^ ^^^ + + onChange2: () => {}, +>onChange2 : () => void +> : ^^^^^^^^^^ +>() => {} : () => void +> : ^^^^^^^^^^ + +}); + +NonGenericComponent({ +>NonGenericComponent({ parentId: [], onChange: (e) => {}, onChange2: () => {},}) : null +> : ^^^^ +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ +>{ parentId: [], onChange: (e) => {}, onChange2: () => {},} : { parentId: never[]; onChange: (e: { id: string; }) => void; onChange2: () => void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + parentId: [], +>parentId : never[] +> : ^^^^^^^ +>[] : never[] +> : ^^^^^^^ + + onChange: (e) => {}, +>onChange : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>(e) => {} : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>e : { id: string; } +> : ^^^^^^ ^^^ + + onChange2: () => {}, +>onChange2 : () => void +> : ^^^^^^^^^^ +>() => {} : () => void +> : ^^^^^^^^^^ + +}); + diff --git a/tests/baselines/reference/contextuallyTypedByDiscriminableUnion3.symbols b/tests/baselines/reference/contextuallyTypedByDiscriminableUnion3.symbols new file mode 100644 index 0000000000000..85ff383e53e0b --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedByDiscriminableUnion3.symbols @@ -0,0 +1,96 @@ +//// [tests/cases/compiler/contextuallyTypedByDiscriminableUnion3.ts] //// + +=== contextuallyTypedByDiscriminableUnion3.ts === +// https://github.com/microsoft/TypeScript/issues/58508 + +type PathSegment = object[]; +>PathSegment : Symbol(PathSegment, Decl(contextuallyTypedByDiscriminableUnion3.ts, 0, 0)) + +type Handle = { +>Handle : Symbol(Handle, Decl(contextuallyTypedByDiscriminableUnion3.ts, 2, 28)) +>TData : Symbol(TData, Decl(contextuallyTypedByDiscriminableUnion3.ts, 4, 12)) + + crumbBuilder: (data: TData) => PathSegment[]; +>crumbBuilder : Symbol(crumbBuilder, Decl(contextuallyTypedByDiscriminableUnion3.ts, 4, 22)) +>data : Symbol(data, Decl(contextuallyTypedByDiscriminableUnion3.ts, 5, 17)) +>TData : Symbol(TData, Decl(contextuallyTypedByDiscriminableUnion3.ts, 4, 12)) +>PathSegment : Symbol(PathSegment, Decl(contextuallyTypedByDiscriminableUnion3.ts, 0, 0)) + +}; + +type Loader = (args: { +>Loader : Symbol(Loader, Decl(contextuallyTypedByDiscriminableUnion3.ts, 6, 2)) +>TData : Symbol(TData, Decl(contextuallyTypedByDiscriminableUnion3.ts, 8, 12)) +>args : Symbol(args, Decl(contextuallyTypedByDiscriminableUnion3.ts, 8, 22)) + + params: Record; +>params : Symbol(params, Decl(contextuallyTypedByDiscriminableUnion3.ts, 8, 29)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +}) => Promise; +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>TData : Symbol(TData, Decl(contextuallyTypedByDiscriminableUnion3.ts, 8, 12)) + +type RouteHandler = +>RouteHandler : Symbol(RouteHandler, Decl(contextuallyTypedByDiscriminableUnion3.ts, 10, 21)) +>TData : Symbol(TData, Decl(contextuallyTypedByDiscriminableUnion3.ts, 12, 18)) + + | { + handle: Handle; +>handle : Symbol(handle, Decl(contextuallyTypedByDiscriminableUnion3.ts, 13, 5)) +>Handle : Symbol(Handle, Decl(contextuallyTypedByDiscriminableUnion3.ts, 2, 28)) + + loader?: never; +>loader : Symbol(loader, Decl(contextuallyTypedByDiscriminableUnion3.ts, 14, 28)) + } + | { + handle: Handle; +>handle : Symbol(handle, Decl(contextuallyTypedByDiscriminableUnion3.ts, 17, 5)) +>Handle : Symbol(Handle, Decl(contextuallyTypedByDiscriminableUnion3.ts, 2, 28)) +>TData : Symbol(TData, Decl(contextuallyTypedByDiscriminableUnion3.ts, 12, 18)) + + loader: Loader; +>loader : Symbol(loader, Decl(contextuallyTypedByDiscriminableUnion3.ts, 18, 28)) +>Loader : Symbol(Loader, Decl(contextuallyTypedByDiscriminableUnion3.ts, 6, 2)) +>TData : Symbol(TData, Decl(contextuallyTypedByDiscriminableUnion3.ts, 12, 18)) + + }; + +const routeHandlerWithoutLoader = { +>routeHandlerWithoutLoader : Symbol(routeHandlerWithoutLoader, Decl(contextuallyTypedByDiscriminableUnion3.ts, 22, 5)) + + handle: { +>handle : Symbol(handle, Decl(contextuallyTypedByDiscriminableUnion3.ts, 22, 35)) + + crumbBuilder: (data) => [], +>crumbBuilder : Symbol(crumbBuilder, Decl(contextuallyTypedByDiscriminableUnion3.ts, 23, 11)) +>data : Symbol(data, Decl(contextuallyTypedByDiscriminableUnion3.ts, 24, 19)) + + }, +} satisfies RouteHandler; +>RouteHandler : Symbol(RouteHandler, Decl(contextuallyTypedByDiscriminableUnion3.ts, 10, 21)) + +const routeHandler = { +>routeHandler : Symbol(routeHandler, Decl(contextuallyTypedByDiscriminableUnion3.ts, 28, 5)) + + loader: async (args) => { +>loader : Symbol(loader, Decl(contextuallyTypedByDiscriminableUnion3.ts, 28, 22)) +>args : Symbol(args, Decl(contextuallyTypedByDiscriminableUnion3.ts, 29, 17)) + + return args.params.userId; +>args.params : Symbol(params, Decl(contextuallyTypedByDiscriminableUnion3.ts, 8, 29)) +>args : Symbol(args, Decl(contextuallyTypedByDiscriminableUnion3.ts, 29, 17)) +>params : Symbol(params, Decl(contextuallyTypedByDiscriminableUnion3.ts, 8, 29)) + + }, + handle: { +>handle : Symbol(handle, Decl(contextuallyTypedByDiscriminableUnion3.ts, 31, 4)) + + crumbBuilder: (data) => [], +>crumbBuilder : Symbol(crumbBuilder, Decl(contextuallyTypedByDiscriminableUnion3.ts, 32, 11)) +>data : Symbol(data, Decl(contextuallyTypedByDiscriminableUnion3.ts, 33, 19)) + + }, +} satisfies RouteHandler; +>RouteHandler : Symbol(RouteHandler, Decl(contextuallyTypedByDiscriminableUnion3.ts, 10, 21)) + diff --git a/tests/baselines/reference/contextuallyTypedByDiscriminableUnion3.types b/tests/baselines/reference/contextuallyTypedByDiscriminableUnion3.types new file mode 100644 index 0000000000000..5cde7e2758a7f --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedByDiscriminableUnion3.types @@ -0,0 +1,132 @@ +//// [tests/cases/compiler/contextuallyTypedByDiscriminableUnion3.ts] //// + +=== contextuallyTypedByDiscriminableUnion3.ts === +// https://github.com/microsoft/TypeScript/issues/58508 + +type PathSegment = object[]; +>PathSegment : PathSegment +> : ^^^^^^^^^^^ + +type Handle = { +>Handle : Handle +> : ^^^^^^^^^^^^^ + + crumbBuilder: (data: TData) => PathSegment[]; +>crumbBuilder : (data: TData) => PathSegment[] +> : ^ ^^ ^^^^^ +>data : TData +> : ^^^^^ + +}; + +type Loader = (args: { +>Loader : Loader +> : ^^^^^^^^^^^^^ +>args : { params: Record; } +> : ^^^^^^^^^^ ^^^ + + params: Record; +>params : Record +> : ^^^^^^^^^^^^^^^^^^^^^^ + +}) => Promise; + +type RouteHandler = +>RouteHandler : RouteHandler +> : ^^^^^^^^^^^^^^^^^^^ + + | { + handle: Handle; +>handle : Handle +> : ^^^^^^^^^^^^^ + + loader?: never; +>loader : undefined +> : ^^^^^^^^^ + } + | { + handle: Handle; +>handle : Handle +> : ^^^^^^^^^^^^^ + + loader: Loader; +>loader : Loader +> : ^^^^^^^^^^^^^ + + }; + +const routeHandlerWithoutLoader = { +>routeHandlerWithoutLoader : { handle: { crumbBuilder: (data: never) => never[]; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ handle: { crumbBuilder: (data) => [], },} satisfies RouteHandler : { handle: { crumbBuilder: (data: never) => never[]; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ handle: { crumbBuilder: (data) => [], },} : { handle: { crumbBuilder: (data: never) => never[]; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ + + handle: { +>handle : { crumbBuilder: (data: never) => never[]; } +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ +>{ crumbBuilder: (data) => [], } : { crumbBuilder: (data: never) => never[]; } +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ + + crumbBuilder: (data) => [], +>crumbBuilder : (data: never) => never[] +> : ^ ^^^^^^^^^^^^^^^^^^^ +>(data) => [] : (data: never) => never[] +> : ^ ^^^^^^^^^^^^^^^^^^^ +>data : never +> : ^^^^^ +>[] : never[] +> : ^^^^^^^ + + }, +} satisfies RouteHandler; + +const routeHandler = { +>routeHandler : { loader: (args: { params: Record; }) => Promise; handle: { crumbBuilder: (data: string) => never[]; }; } +> : ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ loader: async (args) => { return args.params.userId; }, handle: { crumbBuilder: (data) => [], },} satisfies RouteHandler : { loader: (args: { params: Record; }) => Promise; handle: { crumbBuilder: (data: string) => never[]; }; } +> : ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ loader: async (args) => { return args.params.userId; }, handle: { crumbBuilder: (data) => [], },} : { loader: (args: { params: Record; }) => Promise; handle: { crumbBuilder: (data: string) => never[]; }; } +> : ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + loader: async (args) => { +>loader : (args: { params: Record; }) => Promise +> : ^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ +>async (args) => { return args.params.userId; } : (args: { params: Record; }) => Promise +> : ^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ +>args : { params: Record; } +> : ^^^^^^^^^^ ^^^ + + return args.params.userId; +>args.params.userId : string +> : ^^^^^^ +>args.params : Record +> : ^^^^^^^^^^^^^^^^^^^^^^ +>args : { params: Record; } +> : ^^^^^^^^^^ ^^^ +>params : Record +> : ^^^^^^^^^^^^^^^^^^^^^^ +>userId : string +> : ^^^^^^ + + }, + handle: { +>handle : { crumbBuilder: (data: string) => never[]; } +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ +>{ crumbBuilder: (data) => [], } : { crumbBuilder: (data: string) => never[]; } +> : ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ + + crumbBuilder: (data) => [], +>crumbBuilder : (data: string) => never[] +> : ^ ^^^^^^^^^^^^^^^^^^^^ +>(data) => [] : (data: string) => never[] +> : ^ ^^^^^^^^^^^^^^^^^^^^ +>data : string +> : ^^^^^^ +>[] : never[] +> : ^^^^^^^ + + }, +} satisfies RouteHandler; + diff --git a/tests/baselines/reference/contextuallyTypedJsxAttribute3.symbols b/tests/baselines/reference/contextuallyTypedJsxAttribute3.symbols new file mode 100644 index 0000000000000..090094fff9490 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedJsxAttribute3.symbols @@ -0,0 +1,83 @@ +//// [tests/cases/compiler/contextuallyTypedJsxAttribute3.tsx] //// + +=== contextuallyTypedJsxAttribute3.tsx === +/// + +import React from "react"; +>React : Symbol(React, Decl(contextuallyTypedJsxAttribute3.tsx, 2, 6)) + +// https://github.com/microsoft/TypeScript/issues/61095 + +type Props = +>Props : Symbol(Props, Decl(contextuallyTypedJsxAttribute3.tsx, 2, 26)) + + | { + parentId: string[]; +>parentId : Symbol(parentId, Decl(contextuallyTypedJsxAttribute3.tsx, 7, 5)) + + onChange: (event: { id: string }) => void; +>onChange : Symbol(onChange, Decl(contextuallyTypedJsxAttribute3.tsx, 8, 25)) +>event : Symbol(event, Decl(contextuallyTypedJsxAttribute3.tsx, 9, 17)) +>id : Symbol(id, Decl(contextuallyTypedJsxAttribute3.tsx, 9, 25)) + + onChange2: () => void; +>onChange2 : Symbol(onChange2, Decl(contextuallyTypedJsxAttribute3.tsx, 9, 48)) + } + | { + parentId?: never; +>parentId : Symbol(parentId, Decl(contextuallyTypedJsxAttribute3.tsx, 12, 5)) + + onChange: (event: { id: number }) => void; +>onChange : Symbol(onChange, Decl(contextuallyTypedJsxAttribute3.tsx, 13, 23)) +>event : Symbol(event, Decl(contextuallyTypedJsxAttribute3.tsx, 14, 17)) +>id : Symbol(id, Decl(contextuallyTypedJsxAttribute3.tsx, 14, 25)) + + }; + +function NonGenericComponent(props: Props) { +>NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedJsxAttribute3.tsx, 15, 6)) +>props : Symbol(props, Decl(contextuallyTypedJsxAttribute3.tsx, 17, 29)) +>Props : Symbol(Props, Decl(contextuallyTypedJsxAttribute3.tsx, 2, 26)) + + return null; +} + + {}} />; +>NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedJsxAttribute3.tsx, 15, 6)) +>onChange : Symbol(onChange, Decl(contextuallyTypedJsxAttribute3.tsx, 21, 20)) +>e : Symbol(e, Decl(contextuallyTypedJsxAttribute3.tsx, 21, 32)) + +const parentId: string[] = []; +>parentId : Symbol(parentId, Decl(contextuallyTypedJsxAttribute3.tsx, 23, 5)) + +NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedJsxAttribute3.tsx, 15, 6)) + + parentId={parentId} +>parentId : Symbol(parentId, Decl(contextuallyTypedJsxAttribute3.tsx, 25, 20)) +>parentId : Symbol(parentId, Decl(contextuallyTypedJsxAttribute3.tsx, 23, 5)) + + onChange={(e) => {}} +>onChange : Symbol(onChange, Decl(contextuallyTypedJsxAttribute3.tsx, 26, 21)) +>e : Symbol(e, Decl(contextuallyTypedJsxAttribute3.tsx, 27, 13)) + + onChange2={() => {}} +>onChange2 : Symbol(onChange2, Decl(contextuallyTypedJsxAttribute3.tsx, 27, 22)) + +/>; + +NonGenericComponent : Symbol(NonGenericComponent, Decl(contextuallyTypedJsxAttribute3.tsx, 15, 6)) + + parentId={[]} +>parentId : Symbol(parentId, Decl(contextuallyTypedJsxAttribute3.tsx, 31, 20)) + + onChange={(e) => {}} +>onChange : Symbol(onChange, Decl(contextuallyTypedJsxAttribute3.tsx, 32, 15)) +>e : Symbol(e, Decl(contextuallyTypedJsxAttribute3.tsx, 33, 13)) + + onChange2={() => {}} +>onChange2 : Symbol(onChange2, Decl(contextuallyTypedJsxAttribute3.tsx, 33, 22)) + +/>; + diff --git a/tests/baselines/reference/contextuallyTypedJsxAttribute3.types b/tests/baselines/reference/contextuallyTypedJsxAttribute3.types new file mode 100644 index 0000000000000..c585f4f49caf9 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedJsxAttribute3.types @@ -0,0 +1,136 @@ +//// [tests/cases/compiler/contextuallyTypedJsxAttribute3.tsx] //// + +=== Performance Stats === +Assignability cache: 2,500 +Type Count: 10,000 +Instantiation count: 100,000 +Symbol count: 50,000 + +=== contextuallyTypedJsxAttribute3.tsx === +/// + +import React from "react"; +>React : typeof React +> : ^^^^^^^^^^^^ + +// https://github.com/microsoft/TypeScript/issues/61095 + +type Props = +>Props : Props +> : ^^^^^ + + | { + parentId: string[]; +>parentId : string[] +> : ^^^^^^^^ + + onChange: (event: { id: string }) => void; +>onChange : (event: { id: string; }) => void +> : ^ ^^ ^^^^^ +>event : { id: string; } +> : ^^^^^^ ^^^ +>id : string +> : ^^^^^^ + + onChange2: () => void; +>onChange2 : () => void +> : ^^^^^^ + } + | { + parentId?: never; +>parentId : undefined +> : ^^^^^^^^^ + + onChange: (event: { id: number }) => void; +>onChange : (event: { id: number; }) => void +> : ^ ^^ ^^^^^ +>event : { id: number; } +> : ^^^^^^ ^^^ +>id : number +> : ^^^^^^ + + }; + +function NonGenericComponent(props: Props) { +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ +>props : Props +> : ^^^^^ + + return null; +} + + {}} />; +> {}} /> : JSX.Element +> : ^^^^^^^^^^^ +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ +>onChange : (e: { id: number; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>(e) => {} : (e: { id: number; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>e : { id: number; } +> : ^^^^^^ ^^^ + +const parentId: string[] = []; +>parentId : string[] +> : ^^^^^^^^ +>[] : never[] +> : ^^^^^^^ + + {}} onChange2={() => {}}/> : JSX.Element +> : ^^^^^^^^^^^ +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ + + parentId={parentId} +>parentId : string[] +> : ^^^^^^^^ +>parentId : string[] +> : ^^^^^^^^ + + onChange={(e) => {}} +>onChange : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>(e) => {} : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>e : { id: string; } +> : ^^^^^^ ^^^ + + onChange2={() => {}} +>onChange2 : () => void +> : ^^^^^^^^^^ +>() => {} : () => void +> : ^^^^^^^^^^ + +/>; + + {}} onChange2={() => {}}/> : JSX.Element +> : ^^^^^^^^^^^ +>NonGenericComponent : (props: Props) => null +> : ^ ^^ ^^^^^^^^^ + + parentId={[]} +>parentId : never[] +> : ^^^^^^^ +>[] : never[] +> : ^^^^^^^ + + onChange={(e) => {}} +>onChange : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>(e) => {} : (e: { id: string; }) => void +> : ^ ^^^^^^^^ ^^^^^^^^^^^^ +>e : { id: string; } +> : ^^^^^^ ^^^ + + onChange2={() => {}} +>onChange2 : () => void +> : ^^^^^^^^^^ +>() => {} : () => void +> : ^^^^^^^^^^ + +/>; + diff --git a/tests/cases/compiler/contextuallyTypedByDiscriminableUnion2.ts b/tests/cases/compiler/contextuallyTypedByDiscriminableUnion2.ts new file mode 100644 index 0000000000000..1158000019c85 --- /dev/null +++ b/tests/cases/compiler/contextuallyTypedByDiscriminableUnion2.ts @@ -0,0 +1,41 @@ +// @strict: true +// @noEmit: true + +type Props = + | { + parentId: string[]; + onChange: (event: { id: string }) => void; + onChange2: () => void; + } + | { + parentId?: never; + onChange: (event: { id: number }) => void; + }; + +function NonGenericComponent(props: Props) { + return null; +} + +NonGenericComponent({ + onChange: (e) => {}, +}); + +const parentId: string[] = []; + +NonGenericComponent({ + parentId, + onChange: (e) => {}, + onChange2: () => {}, +}); + +NonGenericComponent({ + parentId: parentId, + onChange: (e) => {}, + onChange2: () => {}, +}); + +NonGenericComponent({ + parentId: [], + onChange: (e) => {}, + onChange2: () => {}, +}); diff --git a/tests/cases/compiler/contextuallyTypedByDiscriminableUnion3.ts b/tests/cases/compiler/contextuallyTypedByDiscriminableUnion3.ts new file mode 100644 index 0000000000000..a67829cdb4459 --- /dev/null +++ b/tests/cases/compiler/contextuallyTypedByDiscriminableUnion3.ts @@ -0,0 +1,39 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/58508 + +type PathSegment = object[]; + +type Handle = { + crumbBuilder: (data: TData) => PathSegment[]; +}; + +type Loader = (args: { + params: Record; +}) => Promise; + +type RouteHandler = + | { + handle: Handle; + loader?: never; + } + | { + handle: Handle; + loader: Loader; + }; + +const routeHandlerWithoutLoader = { + handle: { + crumbBuilder: (data) => [], + }, +} satisfies RouteHandler; + +const routeHandler = { + loader: async (args) => { + return args.params.userId; + }, + handle: { + crumbBuilder: (data) => [], + }, +} satisfies RouteHandler; diff --git a/tests/cases/compiler/contextuallyTypedJsxAttribute3.tsx b/tests/cases/compiler/contextuallyTypedJsxAttribute3.tsx new file mode 100644 index 0000000000000..52d1cb91b1f8c --- /dev/null +++ b/tests/cases/compiler/contextuallyTypedJsxAttribute3.tsx @@ -0,0 +1,41 @@ +// @strict: true +// @jsx: react +// @esModuleInterop: true +// @noEmit: true + +/// + +import React from "react"; + +// https://github.com/microsoft/TypeScript/issues/61095 + +type Props = + | { + parentId: string[]; + onChange: (event: { id: string }) => void; + onChange2: () => void; + } + | { + parentId?: never; + onChange: (event: { id: number }) => void; + }; + +function NonGenericComponent(props: Props) { + return null; +} + + {}} />; + +const parentId: string[] = []; + + {}} + onChange2={() => {}} +/>; + + {}} + onChange2={() => {}} +/>;