From cd008554b8ddbc721e3b7a178f33d8dc828c1263 Mon Sep 17 00:00:00 2001 From: Erik Brinkman Date: Fri, 4 Mar 2022 17:07:37 -0500 Subject: [PATCH] Fix contextual discrimination for omitted members The first attempt at fixing this issue caused a large regression on one benchmark. This new PR addresses the regression by caching optional members of union types when they're used for discrimination. This seems to reduce the performance hit at the cost of some extra memory, but it's not clear if the fix is worth this regression. --- src/compiler/checker.ts | 111 +++++++++++++++--- src/compiler/types.ts | 7 ++ .../discriminantPropertyInference.js | 16 ++- .../discriminantPropertyInference.symbols | 73 ++++++++---- .../discriminantPropertyInference.types | 30 ++++- .../tsxDiscriminantPropertyInference.js | 15 ++- .../tsxDiscriminantPropertyInference.symbols | 77 ++++++++---- .../tsxDiscriminantPropertyInference.types | 43 +++++-- .../compiler/discriminantPropertyInference.ts | 12 +- .../tsxDiscriminantPropertyInference.tsx | 13 +- 10 files changed, 315 insertions(+), 82 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6c956a9037887..0732a44e4b991 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26853,34 +26853,107 @@ namespace ts { return false; } + /** + * get discriminators for missing properties on node that are optional for any member of the union context type + * + * Because we know in advance which types each member can potentially + * discriminate against, we only have to choose one property among the + * set that discriminates between the same types in the union. + * + * @internal + */ + function getOptionalPropertyDiscriminators(node: ObjectLiteralExpression | JsxAttributes, contextualType: UnionType): [() => Type, __String][] | undefined { + const nodeSymbolMembers = node?.symbol?.members; + if (!nodeSymbolMembers) { + return undefined; + } + + let discriminantNames = contextualType.discriminantOptionalPropertyNames; + if (!discriminantNames) { + const optionalNames = new Map<__String, number[]>(); + let index = 0; + for (const memberType of contextualType.types) { + for (const s of getPropertiesOfType(memberType)) { + if (s.flags & SymbolFlags.Optional) { + const name = s.escapedName; + const inds = optionalNames.get(name); + inds ? inds.push(index) : optionalNames.set(name, [index]); + } + } + ++index; + } + + const grouped = new Map(); + optionalNames.forEach((inds, escapedName) => { + const key = inds.join(" "); + const names = grouped.get(key); + names ? names.push(escapedName) : grouped.set(key, [escapedName]); + }); + const names: __String[][] = []; + grouped.forEach(nameGroup => names.push(nameGroup)); + discriminantNames = contextualType.discriminantOptionalPropertyNames = names; + } + + return map( + filter( + map(discriminantNames, names => names.find(name => !nodeSymbolMembers.has(name))), + name => !!name // remove undefineds to select only missing members + ), + name => [() => undefinedType, name] as [() => Type, __String], + ); + } + function discriminateContextualTypeByObjectMembers(node: ObjectLiteralExpression, contextualType: UnionType) { - return getMatchingUnionConstituentForObjectLiteral(contextualType, node) || discriminateTypeByDiscriminableItems(contextualType, - concatenate( - map( - filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.PropertyAssignment && isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName)), - prop => ([() => getContextFreeTypeOfExpression((prop as PropertyAssignment).initializer), prop.symbol.escapedName] as [() => Type, __String]) + return ( + getMatchingUnionConstituentForObjectLiteral(contextualType, node) || + discriminateTypeByDiscriminableItems( + contextualType, + concatenate( + map( + filter( + node.properties, + p => + !!p.symbol && + p.kind === SyntaxKind.PropertyAssignment && + isPossiblyDiscriminantValue(p.initializer) && + isDiscriminantProperty(contextualType, p.symbol.escapedName) + ), + prop => + [ + () => getContextFreeTypeOfExpression((prop as PropertyAssignment).initializer), + prop.symbol.escapedName, + ] as [() => Type, __String] + ), + getOptionalPropertyDiscriminators(node, contextualType), ), - map( - filter(getPropertiesOfType(contextualType), s => !!(s.flags & SymbolFlags.Optional) && !!node?.symbol?.members && !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName)), - s => [() => undefinedType, s.escapedName] as [() => Type, __String] - ) - ), - isTypeAssignableTo, - contextualType + isTypeAssignableTo, + contextualType + ) ); } function discriminateContextualTypeByJSXAttributes(node: JsxAttributes, contextualType: UnionType) { - return discriminateTypeByDiscriminableItems(contextualType, + return discriminateTypeByDiscriminableItems( + contextualType, concatenate( map( - filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName) && (!p.initializer || isPossiblyDiscriminantValue(p.initializer))), - prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => getContextFreeTypeOfExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as [() => Type, __String]) + filter( + node.properties, + p => + !!p.symbol && + p.kind === SyntaxKind.JsxAttribute && + isDiscriminantProperty(contextualType, p.symbol.escapedName) && + (!p.initializer || isPossiblyDiscriminantValue(p.initializer)) + ), + prop => + [ + !(prop as JsxAttribute).initializer + ? () => trueType + : () => getContextFreeTypeOfExpression((prop as JsxAttribute).initializer!), + prop.symbol.escapedName, + ] as [() => Type, __String] ), - map( - filter(getPropertiesOfType(contextualType), s => !!(s.flags & SymbolFlags.Optional) && !!node?.symbol?.members && !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName)), - s => [() => undefinedType, s.escapedName] as [() => Type, __String] - ) + getOptionalPropertyDiscriminators(node, contextualType), ), isTypeAssignableTo, contextualType diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1046c79dd3a16..a1756ea7f5398 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5532,6 +5532,13 @@ namespace ts { keyPropertyName?: __String; // Property with unique unit type that exists in every object/intersection in union type /* @internal */ constituentMap?: ESMap; // Constituents keyed by unit type discriminants + /** + * names of discriminant properties that are optional on at least one type + * + * These are grouped by the set of types they discriminate to speed up checks + * @internal + */ + discriminantOptionalPropertyNames?: __String[][]; } export interface IntersectionType extends UnionOrIntersectionType { diff --git a/tests/baselines/reference/discriminantPropertyInference.js b/tests/baselines/reference/discriminantPropertyInference.js index 96f6a106a1136..e65bf3869b0bc 100644 --- a/tests/baselines/reference/discriminantPropertyInference.js +++ b/tests/baselines/reference/discriminantPropertyInference.js @@ -11,7 +11,9 @@ type DiscriminatorFalse = { cb: (x: number) => void; } -type Props = DiscriminatorTrue | DiscriminatorFalse; +type Unrelated = { + val: number; +} declare function f(options: DiscriminatorTrue | DiscriminatorFalse): any; @@ -37,6 +39,14 @@ f({ f({ cb: n => n.toFixed() }); + + +declare function g(options: DiscriminatorTrue | DiscriminatorFalse | Unrelated): any; + +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +g({ + cb: n => n.toFixed() +}); //// [discriminantPropertyInference.js] @@ -60,3 +70,7 @@ f({ f({ cb: function (n) { return n.toFixed(); } }); +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +g({ + cb: function (n) { return n.toFixed(); } +}); diff --git a/tests/baselines/reference/discriminantPropertyInference.symbols b/tests/baselines/reference/discriminantPropertyInference.symbols index 1908bdd984321..938ce86b0c20c 100644 --- a/tests/baselines/reference/discriminantPropertyInference.symbols +++ b/tests/baselines/reference/discriminantPropertyInference.symbols @@ -23,74 +23,97 @@ type DiscriminatorFalse = { >x : Symbol(x, Decl(discriminantPropertyInference.ts, 9, 9)) } -type Props = DiscriminatorTrue | DiscriminatorFalse; ->Props : Symbol(Props, Decl(discriminantPropertyInference.ts, 10, 1)) ->DiscriminatorTrue : Symbol(DiscriminatorTrue, Decl(discriminantPropertyInference.ts, 0, 0)) ->DiscriminatorFalse : Symbol(DiscriminatorFalse, Decl(discriminantPropertyInference.ts, 5, 1)) +type Unrelated = { +>Unrelated : Symbol(Unrelated, Decl(discriminantPropertyInference.ts, 10, 1)) + + val: number; +>val : Symbol(val, Decl(discriminantPropertyInference.ts, 12, 18)) +} declare function f(options: DiscriminatorTrue | DiscriminatorFalse): any; ->f : Symbol(f, Decl(discriminantPropertyInference.ts, 12, 52)) ->options : Symbol(options, Decl(discriminantPropertyInference.ts, 14, 19)) +>f : Symbol(f, Decl(discriminantPropertyInference.ts, 14, 1)) +>options : Symbol(options, Decl(discriminantPropertyInference.ts, 16, 19)) >DiscriminatorTrue : Symbol(DiscriminatorTrue, Decl(discriminantPropertyInference.ts, 0, 0)) >DiscriminatorFalse : Symbol(DiscriminatorFalse, Decl(discriminantPropertyInference.ts, 5, 1)) // simple inference f({ ->f : Symbol(f, Decl(discriminantPropertyInference.ts, 12, 52)) +>f : Symbol(f, Decl(discriminantPropertyInference.ts, 14, 1)) disc: true, ->disc : Symbol(disc, Decl(discriminantPropertyInference.ts, 17, 3)) +>disc : Symbol(disc, Decl(discriminantPropertyInference.ts, 19, 3)) cb: s => parseInt(s) ->cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 18, 15)) ->s : Symbol(s, Decl(discriminantPropertyInference.ts, 19, 7)) +>cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 20, 15)) +>s : Symbol(s, Decl(discriminantPropertyInference.ts, 21, 7)) >parseInt : Symbol(parseInt, Decl(lib.es5.d.ts, --, --)) ->s : Symbol(s, Decl(discriminantPropertyInference.ts, 19, 7)) +>s : Symbol(s, Decl(discriminantPropertyInference.ts, 21, 7)) }); // simple inference f({ ->f : Symbol(f, Decl(discriminantPropertyInference.ts, 12, 52)) +>f : Symbol(f, Decl(discriminantPropertyInference.ts, 14, 1)) disc: false, ->disc : Symbol(disc, Decl(discriminantPropertyInference.ts, 23, 3)) +>disc : Symbol(disc, Decl(discriminantPropertyInference.ts, 25, 3)) cb: n => n.toFixed() ->cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 24, 16)) ->n : Symbol(n, Decl(discriminantPropertyInference.ts, 25, 7)) +>cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 26, 16)) +>n : Symbol(n, Decl(discriminantPropertyInference.ts, 27, 7)) >n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) ->n : Symbol(n, Decl(discriminantPropertyInference.ts, 25, 7)) +>n : Symbol(n, Decl(discriminantPropertyInference.ts, 27, 7)) >toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) }); // simple inference when strict-null-checks are enabled f({ ->f : Symbol(f, Decl(discriminantPropertyInference.ts, 12, 52)) +>f : Symbol(f, Decl(discriminantPropertyInference.ts, 14, 1)) disc: undefined, ->disc : Symbol(disc, Decl(discriminantPropertyInference.ts, 29, 3)) +>disc : Symbol(disc, Decl(discriminantPropertyInference.ts, 31, 3)) >undefined : Symbol(undefined) cb: n => n.toFixed() ->cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 30, 20)) ->n : Symbol(n, Decl(discriminantPropertyInference.ts, 31, 7)) +>cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 32, 20)) +>n : Symbol(n, Decl(discriminantPropertyInference.ts, 33, 7)) >n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) ->n : Symbol(n, Decl(discriminantPropertyInference.ts, 31, 7)) +>n : Symbol(n, Decl(discriminantPropertyInference.ts, 33, 7)) >toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) }); // requires checking type information since discriminator is missing from object f({ ->f : Symbol(f, Decl(discriminantPropertyInference.ts, 12, 52)) +>f : Symbol(f, Decl(discriminantPropertyInference.ts, 14, 1)) + + cb: n => n.toFixed() +>cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 37, 3)) +>n : Symbol(n, Decl(discriminantPropertyInference.ts, 38, 7)) +>n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>n : Symbol(n, Decl(discriminantPropertyInference.ts, 38, 7)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + +}); + + +declare function g(options: DiscriminatorTrue | DiscriminatorFalse | Unrelated): any; +>g : Symbol(g, Decl(discriminantPropertyInference.ts, 39, 3)) +>options : Symbol(options, Decl(discriminantPropertyInference.ts, 42, 19)) +>DiscriminatorTrue : Symbol(DiscriminatorTrue, Decl(discriminantPropertyInference.ts, 0, 0)) +>DiscriminatorFalse : Symbol(DiscriminatorFalse, Decl(discriminantPropertyInference.ts, 5, 1)) +>Unrelated : Symbol(Unrelated, Decl(discriminantPropertyInference.ts, 10, 1)) + +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +g({ +>g : Symbol(g, Decl(discriminantPropertyInference.ts, 39, 3)) cb: n => n.toFixed() ->cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 35, 3)) ->n : Symbol(n, Decl(discriminantPropertyInference.ts, 36, 7)) +>cb : Symbol(cb, Decl(discriminantPropertyInference.ts, 45, 3)) +>n : Symbol(n, Decl(discriminantPropertyInference.ts, 46, 7)) >n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) ->n : Symbol(n, Decl(discriminantPropertyInference.ts, 36, 7)) +>n : Symbol(n, Decl(discriminantPropertyInference.ts, 46, 7)) >toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) }); diff --git a/tests/baselines/reference/discriminantPropertyInference.types b/tests/baselines/reference/discriminantPropertyInference.types index 2ebc6c6a373fe..eef51e40a173f 100644 --- a/tests/baselines/reference/discriminantPropertyInference.types +++ b/tests/baselines/reference/discriminantPropertyInference.types @@ -25,8 +25,12 @@ type DiscriminatorFalse = { >x : number } -type Props = DiscriminatorTrue | DiscriminatorFalse; ->Props : Props +type Unrelated = { +>Unrelated : Unrelated + + val: number; +>val : number +} declare function f(options: DiscriminatorTrue | DiscriminatorFalse): any; >f : (options: DiscriminatorTrue | DiscriminatorFalse) => any @@ -111,3 +115,25 @@ f({ }); + +declare function g(options: DiscriminatorTrue | DiscriminatorFalse | Unrelated): any; +>g : (options: DiscriminatorTrue | DiscriminatorFalse | Unrelated) => any +>options : DiscriminatorTrue | DiscriminatorFalse | Unrelated + +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +g({ +>g({ cb: n => n.toFixed()}) : any +>g : (options: DiscriminatorTrue | DiscriminatorFalse | Unrelated) => any +>{ cb: n => n.toFixed()} : { cb: (n: number) => string; } + + cb: n => n.toFixed() +>cb : (n: number) => string +>n => n.toFixed() : (n: number) => string +>n : number +>n.toFixed() : string +>n.toFixed : (fractionDigits?: number | undefined) => string +>n : number +>toFixed : (fractionDigits?: number | undefined) => string + +}); + diff --git a/tests/baselines/reference/tsxDiscriminantPropertyInference.js b/tests/baselines/reference/tsxDiscriminantPropertyInference.js index b374f4690b0a4..39da73242f056 100644 --- a/tests/baselines/reference/tsxDiscriminantPropertyInference.js +++ b/tests/baselines/reference/tsxDiscriminantPropertyInference.js @@ -14,9 +14,15 @@ type DiscriminatorFalse = { cb: (x: number) => void; } +type Unrelated = { + val: number; +} + type Props = DiscriminatorTrue | DiscriminatorFalse; -declare function Comp(props: DiscriminatorTrue | DiscriminatorFalse): JSX.Element; +type UnrelatedProps = Props | Unrelated; + +declare function Comp(props: Props): JSX.Element; // simple inference void ( parseInt(s)} />); @@ -29,6 +35,11 @@ void ( n.toFixed()} />); // requires checking type information since discriminator is missing from object void ( n.toFixed()} />); + +declare function UnrelatedComp(props: UnrelatedProps): JSX.Element; + +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +void ( n.toFixed()} />); //// [tsxDiscriminantPropertyInference.jsx] @@ -40,3 +51,5 @@ void (); void (); // requires checking type information since discriminator is missing from object void (); +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +void (); diff --git a/tests/baselines/reference/tsxDiscriminantPropertyInference.symbols b/tests/baselines/reference/tsxDiscriminantPropertyInference.symbols index 4b74166e91b63..d7a1f94ae719f 100644 --- a/tests/baselines/reference/tsxDiscriminantPropertyInference.symbols +++ b/tests/baselines/reference/tsxDiscriminantPropertyInference.symbols @@ -29,55 +29,82 @@ type DiscriminatorFalse = { >x : Symbol(x, Decl(tsxDiscriminantPropertyInference.tsx, 12, 9)) } +type Unrelated = { +>Unrelated : Symbol(Unrelated, Decl(tsxDiscriminantPropertyInference.tsx, 13, 1)) + + val: number; +>val : Symbol(val, Decl(tsxDiscriminantPropertyInference.tsx, 15, 18)) +} + type Props = DiscriminatorTrue | DiscriminatorFalse; ->Props : Symbol(Props, Decl(tsxDiscriminantPropertyInference.tsx, 13, 1)) +>Props : Symbol(Props, Decl(tsxDiscriminantPropertyInference.tsx, 17, 1)) >DiscriminatorTrue : Symbol(DiscriminatorTrue, Decl(tsxDiscriminantPropertyInference.tsx, 3, 1)) >DiscriminatorFalse : Symbol(DiscriminatorFalse, Decl(tsxDiscriminantPropertyInference.tsx, 8, 1)) -declare function Comp(props: DiscriminatorTrue | DiscriminatorFalse): JSX.Element; ->Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 15, 52)) ->props : Symbol(props, Decl(tsxDiscriminantPropertyInference.tsx, 17, 22)) ->DiscriminatorTrue : Symbol(DiscriminatorTrue, Decl(tsxDiscriminantPropertyInference.tsx, 3, 1)) ->DiscriminatorFalse : Symbol(DiscriminatorFalse, Decl(tsxDiscriminantPropertyInference.tsx, 8, 1)) +type UnrelatedProps = Props | Unrelated; +>UnrelatedProps : Symbol(UnrelatedProps, Decl(tsxDiscriminantPropertyInference.tsx, 19, 52)) +>Props : Symbol(Props, Decl(tsxDiscriminantPropertyInference.tsx, 17, 1)) +>Unrelated : Symbol(Unrelated, Decl(tsxDiscriminantPropertyInference.tsx, 13, 1)) + +declare function Comp(props: Props): JSX.Element; +>Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 21, 40)) +>props : Symbol(props, Decl(tsxDiscriminantPropertyInference.tsx, 23, 22)) +>Props : Symbol(Props, Decl(tsxDiscriminantPropertyInference.tsx, 17, 1)) >JSX : Symbol(JSX, Decl(tsxDiscriminantPropertyInference.tsx, 0, 0)) >Element : Symbol(JSX.Element, Decl(tsxDiscriminantPropertyInference.tsx, 1, 15)) // simple inference void ( parseInt(s)} />); ->Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 15, 52)) ->disc : Symbol(disc, Decl(tsxDiscriminantPropertyInference.tsx, 20, 11)) ->cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 20, 16)) ->s : Symbol(s, Decl(tsxDiscriminantPropertyInference.tsx, 20, 21)) +>Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 21, 40)) +>disc : Symbol(disc, Decl(tsxDiscriminantPropertyInference.tsx, 26, 11)) +>cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 26, 16)) +>s : Symbol(s, Decl(tsxDiscriminantPropertyInference.tsx, 26, 21)) >parseInt : Symbol(parseInt, Decl(lib.es5.d.ts, --, --)) ->s : Symbol(s, Decl(tsxDiscriminantPropertyInference.tsx, 20, 21)) +>s : Symbol(s, Decl(tsxDiscriminantPropertyInference.tsx, 26, 21)) // simple inference void ( n.toFixed()} />); ->Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 15, 52)) ->disc : Symbol(disc, Decl(tsxDiscriminantPropertyInference.tsx, 23, 11)) ->cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 23, 24)) ->n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 23, 29)) +>Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 21, 40)) +>disc : Symbol(disc, Decl(tsxDiscriminantPropertyInference.tsx, 29, 11)) +>cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 29, 24)) +>n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 29, 29)) >n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) ->n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 23, 29)) +>n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 29, 29)) >toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) // simple inference when strict-null-checks are enabled void ( n.toFixed()} />); ->Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 15, 52)) ->disc : Symbol(disc, Decl(tsxDiscriminantPropertyInference.tsx, 26, 11)) +>Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 21, 40)) +>disc : Symbol(disc, Decl(tsxDiscriminantPropertyInference.tsx, 32, 11)) >undefined : Symbol(undefined) ->cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 26, 28)) ->n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 26, 33)) +>cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 32, 28)) +>n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 32, 33)) >n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) ->n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 26, 33)) +>n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 32, 33)) >toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) // requires checking type information since discriminator is missing from object void ( n.toFixed()} />); ->Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 15, 52)) ->cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 29, 11)) ->n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 29, 16)) +>Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 21, 40)) +>cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 35, 11)) +>n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 35, 16)) +>n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 35, 16)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + +declare function UnrelatedComp(props: UnrelatedProps): JSX.Element; +>UnrelatedComp : Symbol(UnrelatedComp, Decl(tsxDiscriminantPropertyInference.tsx, 35, 38)) +>props : Symbol(props, Decl(tsxDiscriminantPropertyInference.tsx, 37, 31)) +>UnrelatedProps : Symbol(UnrelatedProps, Decl(tsxDiscriminantPropertyInference.tsx, 19, 52)) +>JSX : Symbol(JSX, Decl(tsxDiscriminantPropertyInference.tsx, 0, 0)) +>Element : Symbol(JSX.Element, Decl(tsxDiscriminantPropertyInference.tsx, 1, 15)) + +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +void ( n.toFixed()} />); +>Comp : Symbol(Comp, Decl(tsxDiscriminantPropertyInference.tsx, 21, 40)) +>cb : Symbol(cb, Decl(tsxDiscriminantPropertyInference.tsx, 40, 11)) +>n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 40, 16)) >n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) ->n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 29, 16)) +>n : Symbol(n, Decl(tsxDiscriminantPropertyInference.tsx, 40, 16)) >toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) diff --git a/tests/baselines/reference/tsxDiscriminantPropertyInference.types b/tests/baselines/reference/tsxDiscriminantPropertyInference.types index 63932bd18d963..49a89d29ef7ce 100644 --- a/tests/baselines/reference/tsxDiscriminantPropertyInference.types +++ b/tests/baselines/reference/tsxDiscriminantPropertyInference.types @@ -28,12 +28,22 @@ type DiscriminatorFalse = { >x : number } +type Unrelated = { +>Unrelated : Unrelated + + val: number; +>val : number +} + type Props = DiscriminatorTrue | DiscriminatorFalse; >Props : Props -declare function Comp(props: DiscriminatorTrue | DiscriminatorFalse): JSX.Element; ->Comp : (props: DiscriminatorTrue | DiscriminatorFalse) => JSX.Element ->props : DiscriminatorTrue | DiscriminatorFalse +type UnrelatedProps = Props | Unrelated; +>UnrelatedProps : UnrelatedProps + +declare function Comp(props: Props): JSX.Element; +>Comp : (props: Props) => JSX.Element +>props : Props >JSX : any // simple inference @@ -41,7 +51,7 @@ void ( parseInt(s)} />); >void ( parseInt(s)} />) : undefined >( parseInt(s)} />) : JSX.Element > parseInt(s)} /> : JSX.Element ->Comp : (props: DiscriminatorTrue | DiscriminatorFalse) => JSX.Element +>Comp : (props: Props) => JSX.Element >disc : true >cb : (s: string) => number >s => parseInt(s) : (s: string) => number @@ -55,7 +65,7 @@ void ( n.toFixed()} />); >void ( n.toFixed()} />) : undefined >( n.toFixed()} />) : JSX.Element > n.toFixed()} /> : JSX.Element ->Comp : (props: DiscriminatorTrue | DiscriminatorFalse) => JSX.Element +>Comp : (props: Props) => JSX.Element >disc : false >false : false >cb : (n: number) => string @@ -71,7 +81,7 @@ void ( n.toFixed()} />); >void ( n.toFixed()} />) : undefined >( n.toFixed()} />) : JSX.Element > n.toFixed()} /> : JSX.Element ->Comp : (props: DiscriminatorTrue | DiscriminatorFalse) => JSX.Element +>Comp : (props: Props) => JSX.Element >disc : undefined >undefined : undefined >cb : (n: number) => string @@ -87,7 +97,26 @@ void ( n.toFixed()} />); >void ( n.toFixed()} />) : undefined >( n.toFixed()} />) : JSX.Element > n.toFixed()} /> : JSX.Element ->Comp : (props: DiscriminatorTrue | DiscriminatorFalse) => JSX.Element +>Comp : (props: Props) => JSX.Element +>cb : (n: number) => string +>n => n.toFixed() : (n: number) => string +>n : number +>n.toFixed() : string +>n.toFixed : (fractionDigits?: number | undefined) => string +>n : number +>toFixed : (fractionDigits?: number | undefined) => string + +declare function UnrelatedComp(props: UnrelatedProps): JSX.Element; +>UnrelatedComp : (props: UnrelatedProps) => JSX.Element +>props : UnrelatedProps +>JSX : any + +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +void ( n.toFixed()} />); +>void ( n.toFixed()} />) : undefined +>( n.toFixed()} />) : JSX.Element +> n.toFixed()} /> : JSX.Element +>Comp : (props: Props) => JSX.Element >cb : (n: number) => string >n => n.toFixed() : (n: number) => string >n : number diff --git a/tests/cases/compiler/discriminantPropertyInference.ts b/tests/cases/compiler/discriminantPropertyInference.ts index 5aeb0dd0c0d7a..e2b39488b218d 100644 --- a/tests/cases/compiler/discriminantPropertyInference.ts +++ b/tests/cases/compiler/discriminantPropertyInference.ts @@ -13,7 +13,9 @@ type DiscriminatorFalse = { cb: (x: number) => void; } -type Props = DiscriminatorTrue | DiscriminatorFalse; +type Unrelated = { + val: number; +} declare function f(options: DiscriminatorTrue | DiscriminatorFalse): any; @@ -39,3 +41,11 @@ f({ f({ cb: n => n.toFixed() }); + + +declare function g(options: DiscriminatorTrue | DiscriminatorFalse | Unrelated): any; + +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +g({ + cb: n => n.toFixed() +}); diff --git a/tests/cases/compiler/tsxDiscriminantPropertyInference.tsx b/tests/cases/compiler/tsxDiscriminantPropertyInference.tsx index d4db0f82d50a0..d4fe3baf37851 100644 --- a/tests/cases/compiler/tsxDiscriminantPropertyInference.tsx +++ b/tests/cases/compiler/tsxDiscriminantPropertyInference.tsx @@ -17,9 +17,15 @@ type DiscriminatorFalse = { cb: (x: number) => void; } +type Unrelated = { + val: number; +} + type Props = DiscriminatorTrue | DiscriminatorFalse; -declare function Comp(props: DiscriminatorTrue | DiscriminatorFalse): JSX.Element; +type UnrelatedProps = Props | Unrelated; + +declare function Comp(props: Props): JSX.Element; // simple inference void ( parseInt(s)} />); @@ -32,3 +38,8 @@ void ( n.toFixed()} />); // requires checking type information since discriminator is missing from object void ( n.toFixed()} />); + +declare function UnrelatedComp(props: UnrelatedProps): JSX.Element; + +// requires checking properties of all types, rather than properties of just the union type (e.g. only intersection) +void ( n.toFixed()} />);