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()} />);