Fixed JSX attributes discriminating based on optional children (#53980)
This commit is contained in:
Родитель
a956bbc831
Коммит
2cbfb51ebb
|
@ -29336,6 +29336,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
|||
}
|
||||
|
||||
function discriminateContextualTypeByJSXAttributes(node: JsxAttributes, contextualType: UnionType) {
|
||||
const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node));
|
||||
return discriminateTypeByDiscriminableItems(contextualType,
|
||||
concatenate(
|
||||
map(
|
||||
|
@ -29343,7 +29344,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
|||
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)),
|
||||
filter(getPropertiesOfType(contextualType), s => {
|
||||
if (!(s.flags & SymbolFlags.Optional) || !node?.symbol?.members) {
|
||||
return false;
|
||||
}
|
||||
const element = node.parent.parent;
|
||||
if (s.escapedName === jsxChildrenPropertyName && isJsxElement(element) && getSemanticJsxChildren(element.children).length) {
|
||||
return false;
|
||||
}
|
||||
return !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName);
|
||||
}),
|
||||
s => [() => undefinedType, s.escapedName] as [() => Type, __String]
|
||||
)
|
||||
),
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
=== tests/cases/compiler/contextuallyTypedJsxChildren.tsx ===
|
||||
/// <reference path="react16.d.ts" />
|
||||
|
||||
import React from 'react';
|
||||
>React : Symbol(React, Decl(contextuallyTypedJsxChildren.tsx, 2, 6))
|
||||
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/53941
|
||||
declare namespace DropdownMenu {
|
||||
>DropdownMenu : Symbol(DropdownMenu, Decl(contextuallyTypedJsxChildren.tsx, 2, 26), Decl(contextuallyTypedJsxChildren.tsx, 23, 13))
|
||||
|
||||
interface BaseProps {
|
||||
>BaseProps : Symbol(BaseProps, Decl(contextuallyTypedJsxChildren.tsx, 5, 32))
|
||||
|
||||
icon: string;
|
||||
>icon : Symbol(BaseProps.icon, Decl(contextuallyTypedJsxChildren.tsx, 6, 23))
|
||||
|
||||
label: string;
|
||||
>label : Symbol(BaseProps.label, Decl(contextuallyTypedJsxChildren.tsx, 7, 17))
|
||||
}
|
||||
interface PropsWithChildren extends BaseProps {
|
||||
>PropsWithChildren : Symbol(PropsWithChildren, Decl(contextuallyTypedJsxChildren.tsx, 9, 3))
|
||||
>BaseProps : Symbol(BaseProps, Decl(contextuallyTypedJsxChildren.tsx, 5, 32))
|
||||
|
||||
children(props: { onClose: () => void }): JSX.Element;
|
||||
>children : Symbol(PropsWithChildren.children, Decl(contextuallyTypedJsxChildren.tsx, 10, 49))
|
||||
>props : Symbol(props, Decl(contextuallyTypedJsxChildren.tsx, 11, 13))
|
||||
>onClose : Symbol(onClose, Decl(contextuallyTypedJsxChildren.tsx, 11, 21))
|
||||
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12))
|
||||
>Element : Symbol(JSX.Element, Decl(react16.d.ts, 2494, 23))
|
||||
|
||||
controls?: never | undefined;
|
||||
>controls : Symbol(PropsWithChildren.controls, Decl(contextuallyTypedJsxChildren.tsx, 11, 58))
|
||||
}
|
||||
interface PropsWithControls extends BaseProps {
|
||||
>PropsWithControls : Symbol(PropsWithControls, Decl(contextuallyTypedJsxChildren.tsx, 13, 3))
|
||||
>BaseProps : Symbol(BaseProps, Decl(contextuallyTypedJsxChildren.tsx, 5, 32))
|
||||
|
||||
controls: Control[];
|
||||
>controls : Symbol(PropsWithControls.controls, Decl(contextuallyTypedJsxChildren.tsx, 14, 49))
|
||||
>Control : Symbol(Control, Decl(contextuallyTypedJsxChildren.tsx, 17, 3))
|
||||
|
||||
children?: never | undefined;
|
||||
>children : Symbol(PropsWithControls.children, Decl(contextuallyTypedJsxChildren.tsx, 15, 24))
|
||||
}
|
||||
interface Control {
|
||||
>Control : Symbol(Control, Decl(contextuallyTypedJsxChildren.tsx, 17, 3))
|
||||
|
||||
title: string;
|
||||
>title : Symbol(Control.title, Decl(contextuallyTypedJsxChildren.tsx, 18, 21))
|
||||
}
|
||||
type Props = PropsWithChildren | PropsWithControls;
|
||||
>Props : Symbol(Props, Decl(contextuallyTypedJsxChildren.tsx, 20, 3))
|
||||
>PropsWithChildren : Symbol(PropsWithChildren, Decl(contextuallyTypedJsxChildren.tsx, 9, 3))
|
||||
>PropsWithControls : Symbol(PropsWithControls, Decl(contextuallyTypedJsxChildren.tsx, 13, 3))
|
||||
}
|
||||
declare const DropdownMenu: React.ComponentType<DropdownMenu.Props>;
|
||||
>DropdownMenu : Symbol(DropdownMenu, Decl(contextuallyTypedJsxChildren.tsx, 2, 26), Decl(contextuallyTypedJsxChildren.tsx, 23, 13))
|
||||
>React : Symbol(React, Decl(contextuallyTypedJsxChildren.tsx, 2, 6))
|
||||
>ComponentType : Symbol(React.ComponentType, Decl(react16.d.ts, 117, 60))
|
||||
>DropdownMenu : Symbol(DropdownMenu, Decl(contextuallyTypedJsxChildren.tsx, 2, 26), Decl(contextuallyTypedJsxChildren.tsx, 23, 13))
|
||||
>Props : Symbol(DropdownMenu.Props, Decl(contextuallyTypedJsxChildren.tsx, 20, 3))
|
||||
|
||||
<DropdownMenu icon="move" label="Select a direction">
|
||||
>DropdownMenu : Symbol(DropdownMenu, Decl(contextuallyTypedJsxChildren.tsx, 2, 26), Decl(contextuallyTypedJsxChildren.tsx, 23, 13))
|
||||
>icon : Symbol(icon, Decl(contextuallyTypedJsxChildren.tsx, 25, 13))
|
||||
>label : Symbol(label, Decl(contextuallyTypedJsxChildren.tsx, 25, 25))
|
||||
|
||||
{({ onClose }) => (
|
||||
>onClose : Symbol(onClose, Decl(contextuallyTypedJsxChildren.tsx, 26, 5))
|
||||
|
||||
<div>
|
||||
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
|
||||
|
||||
<button onClick={onClose}>Click me</button>
|
||||
>button : Symbol(JSX.IntrinsicElements.button, Decl(react16.d.ts, 2532, 96))
|
||||
>onClick : Symbol(onClick, Decl(contextuallyTypedJsxChildren.tsx, 28, 13))
|
||||
>onClose : Symbol(onClose, Decl(contextuallyTypedJsxChildren.tsx, 26, 5))
|
||||
>button : Symbol(JSX.IntrinsicElements.button, Decl(react16.d.ts, 2532, 96))
|
||||
|
||||
</div>
|
||||
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
|
||||
|
||||
)}
|
||||
</DropdownMenu>;
|
||||
>DropdownMenu : Symbol(DropdownMenu, Decl(contextuallyTypedJsxChildren.tsx, 2, 26), Decl(contextuallyTypedJsxChildren.tsx, 23, 13))
|
||||
|
||||
<DropdownMenu
|
||||
>DropdownMenu : Symbol(DropdownMenu, Decl(contextuallyTypedJsxChildren.tsx, 2, 26), Decl(contextuallyTypedJsxChildren.tsx, 23, 13))
|
||||
|
||||
icon="move"
|
||||
>icon : Symbol(icon, Decl(contextuallyTypedJsxChildren.tsx, 33, 13))
|
||||
|
||||
label="Select a direction"
|
||||
>label : Symbol(label, Decl(contextuallyTypedJsxChildren.tsx, 34, 13))
|
||||
|
||||
children={({ onClose }) => (
|
||||
>children : Symbol(children, Decl(contextuallyTypedJsxChildren.tsx, 35, 28))
|
||||
>onClose : Symbol(onClose, Decl(contextuallyTypedJsxChildren.tsx, 36, 14))
|
||||
|
||||
<div>
|
||||
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
|
||||
|
||||
<button onClick={onClose}>Click me</button>
|
||||
>button : Symbol(JSX.IntrinsicElements.button, Decl(react16.d.ts, 2532, 96))
|
||||
>onClick : Symbol(onClick, Decl(contextuallyTypedJsxChildren.tsx, 38, 13))
|
||||
>onClose : Symbol(onClose, Decl(contextuallyTypedJsxChildren.tsx, 36, 14))
|
||||
>button : Symbol(JSX.IntrinsicElements.button, Decl(react16.d.ts, 2532, 96))
|
||||
|
||||
</div>
|
||||
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
|
||||
|
||||
)}
|
||||
/>;
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
=== tests/cases/compiler/contextuallyTypedJsxChildren.tsx ===
|
||||
/// <reference path="react16.d.ts" />
|
||||
|
||||
import React from 'react';
|
||||
>React : typeof React
|
||||
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/53941
|
||||
declare namespace DropdownMenu {
|
||||
interface BaseProps {
|
||||
icon: string;
|
||||
>icon : string
|
||||
|
||||
label: string;
|
||||
>label : string
|
||||
}
|
||||
interface PropsWithChildren extends BaseProps {
|
||||
children(props: { onClose: () => void }): JSX.Element;
|
||||
>children : (props: { onClose: () => void;}) => JSX.Element
|
||||
>props : { onClose: () => void; }
|
||||
>onClose : () => void
|
||||
>JSX : any
|
||||
|
||||
controls?: never | undefined;
|
||||
>controls : undefined
|
||||
}
|
||||
interface PropsWithControls extends BaseProps {
|
||||
controls: Control[];
|
||||
>controls : Control[]
|
||||
|
||||
children?: never | undefined;
|
||||
>children : undefined
|
||||
}
|
||||
interface Control {
|
||||
title: string;
|
||||
>title : string
|
||||
}
|
||||
type Props = PropsWithChildren | PropsWithControls;
|
||||
>Props : PropsWithChildren | PropsWithControls
|
||||
}
|
||||
declare const DropdownMenu: React.ComponentType<DropdownMenu.Props>;
|
||||
>DropdownMenu : React.ComponentType<DropdownMenu.Props>
|
||||
>React : any
|
||||
>DropdownMenu : any
|
||||
|
||||
<DropdownMenu icon="move" label="Select a direction">
|
||||
><DropdownMenu icon="move" label="Select a direction"> {({ onClose }) => ( <div> <button onClick={onClose}>Click me</button> </div> )}</DropdownMenu> : JSX.Element
|
||||
>DropdownMenu : React.ComponentType<DropdownMenu.Props>
|
||||
>icon : string
|
||||
>label : string
|
||||
|
||||
{({ onClose }) => (
|
||||
>({ onClose }) => ( <div> <button onClick={onClose}>Click me</button> </div> ) : ({ onClose }: { onClose: () => void; }) => JSX.Element
|
||||
>onClose : () => void
|
||||
>( <div> <button onClick={onClose}>Click me</button> </div> ) : JSX.Element
|
||||
|
||||
<div>
|
||||
><div> <button onClick={onClose}>Click me</button> </div> : JSX.Element
|
||||
>div : any
|
||||
|
||||
<button onClick={onClose}>Click me</button>
|
||||
><button onClick={onClose}>Click me</button> : JSX.Element
|
||||
>button : any
|
||||
>onClick : () => void
|
||||
>onClose : () => void
|
||||
>button : any
|
||||
|
||||
</div>
|
||||
>div : any
|
||||
|
||||
)}
|
||||
</DropdownMenu>;
|
||||
>DropdownMenu : React.ComponentType<DropdownMenu.Props>
|
||||
|
||||
<DropdownMenu
|
||||
><DropdownMenu icon="move" label="Select a direction" children={({ onClose }) => ( <div> <button onClick={onClose}>Click me</button> </div> )}/> : JSX.Element
|
||||
>DropdownMenu : React.ComponentType<DropdownMenu.Props>
|
||||
|
||||
icon="move"
|
||||
>icon : string
|
||||
|
||||
label="Select a direction"
|
||||
>label : string
|
||||
|
||||
children={({ onClose }) => (
|
||||
>children : ({ onClose }: { onClose: () => void; }) => JSX.Element
|
||||
>({ onClose }) => ( <div> <button onClick={onClose}>Click me</button> </div> ) : ({ onClose }: { onClose: () => void; }) => JSX.Element
|
||||
>onClose : () => void
|
||||
>( <div> <button onClick={onClose}>Click me</button> </div> ) : JSX.Element
|
||||
|
||||
<div>
|
||||
><div> <button onClick={onClose}>Click me</button> </div> : JSX.Element
|
||||
>div : any
|
||||
|
||||
<button onClick={onClose}>Click me</button>
|
||||
><button onClick={onClose}>Click me</button> : JSX.Element
|
||||
>button : any
|
||||
>onClick : () => void
|
||||
>onClose : () => void
|
||||
>button : any
|
||||
|
||||
</div>
|
||||
>div : any
|
||||
|
||||
)}
|
||||
/>;
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// @strict: true
|
||||
// @jsx: react
|
||||
// @esModuleInterop: true
|
||||
// @noEmit: true
|
||||
|
||||
/// <reference path="/.lib/react16.d.ts" />
|
||||
|
||||
import React from 'react';
|
||||
|
||||
// repro from https://github.com/microsoft/TypeScript/issues/53941
|
||||
declare namespace DropdownMenu {
|
||||
interface BaseProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
}
|
||||
interface PropsWithChildren extends BaseProps {
|
||||
children(props: { onClose: () => void }): JSX.Element;
|
||||
controls?: never | undefined;
|
||||
}
|
||||
interface PropsWithControls extends BaseProps {
|
||||
controls: Control[];
|
||||
children?: never | undefined;
|
||||
}
|
||||
interface Control {
|
||||
title: string;
|
||||
}
|
||||
type Props = PropsWithChildren | PropsWithControls;
|
||||
}
|
||||
declare const DropdownMenu: React.ComponentType<DropdownMenu.Props>;
|
||||
|
||||
<DropdownMenu icon="move" label="Select a direction">
|
||||
{({ onClose }) => (
|
||||
<div>
|
||||
<button onClick={onClose}>Click me</button>
|
||||
</div>
|
||||
)}
|
||||
</DropdownMenu>;
|
||||
|
||||
<DropdownMenu
|
||||
icon="move"
|
||||
label="Select a direction"
|
||||
children={({ onClose }) => (
|
||||
<div>
|
||||
<button onClick={onClose}>Click me</button>
|
||||
</div>
|
||||
)}
|
||||
/>;
|
Загрузка…
Ссылка в новой задаче