From 99d6b0d308f6b9a33e61325d093c7354e7ae28af Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 17 Jan 2018 15:20:09 -0800 Subject: [PATCH] Actually get the apparent type of intersection members when calculating intersection apparent types (#21133) * Actually get the apparent type of intersection members when calculating intersection apparent types * Add nonjsx variant * Fix nit --- src/compiler/checker.ts | 13 +-- ...tionOfTypeVariableHasApparentSignatures.js | 25 ++++++ ...fTypeVariableHasApparentSignatures.symbols | 41 +++++++++ ...nOfTypeVariableHasApparentSignatures.types | 46 ++++++++++ .../reference/jsxCallbackWithDestructuring.js | 52 ++++++++++++ .../jsxCallbackWithDestructuring.symbols | 81 ++++++++++++++++++ .../jsxCallbackWithDestructuring.types | 83 +++++++++++++++++++ ...ionComponentsWithTypeArguments4.errors.txt | 4 +- ...tionOfTypeVariableHasApparentSignatures.ts | 17 ++++ .../compiler/jsxCallbackWithDestructuring.tsx | 31 +++++++ 10 files changed, 385 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.js create mode 100644 tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.symbols create mode 100644 tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.types create mode 100644 tests/baselines/reference/jsxCallbackWithDestructuring.js create mode 100644 tests/baselines/reference/jsxCallbackWithDestructuring.symbols create mode 100644 tests/baselines/reference/jsxCallbackWithDestructuring.types create mode 100644 tests/cases/compiler/intersectionOfTypeVariableHasApparentSignatures.ts create mode 100644 tests/cases/compiler/jsxCallbackWithDestructuring.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0e4525c9f2d..c1ad93b04aa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5416,18 +5416,19 @@ namespace ts { return symbol; } - function getTypeWithThisArgument(type: Type, thisArgument?: Type): Type { + function getTypeWithThisArgument(type: Type, thisArgument?: Type, needApparentType?: boolean): Type { if (getObjectFlags(type) & ObjectFlags.Reference) { const target = (type).target; const typeArguments = (type).typeArguments; if (length(target.typeParameters) === length(typeArguments)) { - return createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType])); + const ref = createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType])); + return needApparentType ? getApparentType(ref) : ref; } } else if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(map((type).types, t => getTypeWithThisArgument(t, thisArgument))); + return getIntersectionType(map((type).types, t => getTypeWithThisArgument(t, thisArgument, needApparentType))); } - return type; + return needApparentType ? getApparentType(type) : type; } function resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: TypeParameter[], typeArguments: Type[]) { @@ -6107,7 +6108,7 @@ namespace ts { } function getApparentTypeOfIntersectionType(type: IntersectionType) { - return type.resolvedApparentType || (type.resolvedApparentType = getTypeWithThisArgument(type, type)); + return type.resolvedApparentType || (type.resolvedApparentType = getTypeWithThisArgument(type, type, /*apparentType*/ true)); } function getResolvedTypeParameterDefault(typeParameter: TypeParameter): Type | undefined { @@ -14068,7 +14069,7 @@ namespace ts { // If the given type is an object or union type with a single signature, and if that signature has at // least as many parameters as the given function, return the signature. Otherwise return undefined. function getContextualCallSignature(type: Type, node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature { - const signatures = getSignaturesOfStructuredType(type, SignatureKind.Call); + const signatures = getSignaturesOfType(type, SignatureKind.Call); if (signatures.length === 1) { const signature = signatures[0]; if (!isAritySmaller(signature, node)) { diff --git a/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.js b/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.js new file mode 100644 index 00000000000..b0f224b5591 --- /dev/null +++ b/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.js @@ -0,0 +1,25 @@ +//// [intersectionOfTypeVariableHasApparentSignatures.ts] +interface Component

{ + props: Readonly

& Readonly<{ children?: {} }>; +} + +interface Props { + children?: (items: {x: number}) => void +} + +declare function f(i: Component): void; + +f({ + props: { + children: (({ x }) => { }) + } +}); + +//// [intersectionOfTypeVariableHasApparentSignatures.js] +f({ + props: { + children: (function (_a) { + var x = _a.x; + }) + } +}); diff --git a/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.symbols b/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.symbols new file mode 100644 index 00000000000..8dc4781c865 --- /dev/null +++ b/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.symbols @@ -0,0 +1,41 @@ +=== tests/cases/compiler/intersectionOfTypeVariableHasApparentSignatures.ts === +interface Component

{ +>Component : Symbol(Component, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 0, 0)) +>P : Symbol(P, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 0, 20)) + + props: Readonly

& Readonly<{ children?: {} }>; +>props : Symbol(Component.props, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 0, 24)) +>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --)) +>P : Symbol(P, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 0, 20)) +>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --)) +>children : Symbol(children, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 1, 35)) +} + +interface Props { +>Props : Symbol(Props, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 2, 1)) + + children?: (items: {x: number}) => void +>children : Symbol(Props.children, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 4, 17)) +>items : Symbol(items, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 5, 16)) +>x : Symbol(x, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 5, 24)) +} + +declare function f(i: Component): void; +>f : Symbol(f, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 6, 1)) +>T : Symbol(T, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 8, 19)) +>Props : Symbol(Props, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 2, 1)) +>i : Symbol(i, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 8, 36)) +>Component : Symbol(Component, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 0, 0)) +>T : Symbol(T, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 8, 19)) + +f({ +>f : Symbol(f, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 6, 1)) + + props: { +>props : Symbol(props, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 10, 3)) + + children: (({ x }) => { }) +>children : Symbol(children, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 11, 12)) +>x : Symbol(x, Decl(intersectionOfTypeVariableHasApparentSignatures.ts, 12, 21)) + } +}); diff --git a/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.types b/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.types new file mode 100644 index 00000000000..927839de2e7 --- /dev/null +++ b/tests/baselines/reference/intersectionOfTypeVariableHasApparentSignatures.types @@ -0,0 +1,46 @@ +=== tests/cases/compiler/intersectionOfTypeVariableHasApparentSignatures.ts === +interface Component

{ +>Component : Component

+>P : P + + props: Readonly

& Readonly<{ children?: {} }>; +>props : Readonly

& Readonly<{ children?: {} | undefined; }> +>Readonly : Readonly +>P : P +>Readonly : Readonly +>children : {} | undefined +} + +interface Props { +>Props : Props + + children?: (items: {x: number}) => void +>children : ((items: { x: number; }) => void) | undefined +>items : { x: number; } +>x : number +} + +declare function f(i: Component): void; +>f : (i: Component) => void +>T : T +>Props : Props +>i : Component +>Component : Component

+>T : T + +f({ +>f({ props: { children: (({ x }) => { }) }}) : void +>f : (i: Component) => void +>{ props: { children: (({ x }) => { }) }} : { props: { children: ({ x }: { x: number; }) => void; }; } + + props: { +>props : { children: ({ x }: { x: number; }) => void; } +>{ children: (({ x }) => { }) } : { children: ({ x }: { x: number; }) => void; } + + children: (({ x }) => { }) +>children : ({ x }: { x: number; }) => void +>(({ x }) => { }) : ({ x }: { x: number; }) => void +>({ x }) => { } : ({ x }: { x: number; }) => void +>x : number + } +}); diff --git a/tests/baselines/reference/jsxCallbackWithDestructuring.js b/tests/baselines/reference/jsxCallbackWithDestructuring.js new file mode 100644 index 00000000000..f8f7173aa30 --- /dev/null +++ b/tests/baselines/reference/jsxCallbackWithDestructuring.js @@ -0,0 +1,52 @@ +//// [jsxCallbackWithDestructuring.tsx] +// minimal component +interface Component

{ } +declare class Component { + constructor(props: P, context?: any); + render(): {}; + props: Readonly<{ children?: {} }> & Readonly

; +} + +declare global { + namespace JSX { + interface Element { } + interface ElementClass { + render(): {}; + } + interface ElementAttributesProperty { props: {}; } + interface ElementChildrenAttribute { children: {}; } + interface IntrinsicAttributes { } + interface IntrinsicClassAttributes { } + } +} + +export interface RouteProps { + children?: (props: { x: number }) => any; +} +export class MyComponent extends Component { } + {}}/> + +//// [jsxCallbackWithDestructuring.jsx] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var MyComponent = /** @class */ (function (_super) { + __extends(MyComponent, _super); + function MyComponent() { + return _super !== null && _super.apply(this, arguments) || this; + } + return MyComponent; +}(Component)); +exports.MyComponent = MyComponent; +; diff --git a/tests/baselines/reference/jsxCallbackWithDestructuring.symbols b/tests/baselines/reference/jsxCallbackWithDestructuring.symbols new file mode 100644 index 00000000000..d879f256e13 --- /dev/null +++ b/tests/baselines/reference/jsxCallbackWithDestructuring.symbols @@ -0,0 +1,81 @@ +=== tests/cases/compiler/jsxCallbackWithDestructuring.tsx === +// minimal component +interface Component

{ } +>Component : Symbol(Component, Decl(jsxCallbackWithDestructuring.tsx, 0, 0), Decl(jsxCallbackWithDestructuring.tsx, 1, 39)) +>P : Symbol(P, Decl(jsxCallbackWithDestructuring.tsx, 1, 20), Decl(jsxCallbackWithDestructuring.tsx, 2, 24)) +>S : Symbol(S, Decl(jsxCallbackWithDestructuring.tsx, 1, 27), Decl(jsxCallbackWithDestructuring.tsx, 2, 26)) + +declare class Component { +>Component : Symbol(Component, Decl(jsxCallbackWithDestructuring.tsx, 0, 0), Decl(jsxCallbackWithDestructuring.tsx, 1, 39)) +>P : Symbol(P, Decl(jsxCallbackWithDestructuring.tsx, 1, 20), Decl(jsxCallbackWithDestructuring.tsx, 2, 24)) +>S : Symbol(S, Decl(jsxCallbackWithDestructuring.tsx, 1, 27), Decl(jsxCallbackWithDestructuring.tsx, 2, 26)) + + constructor(props: P, context?: any); +>props : Symbol(props, Decl(jsxCallbackWithDestructuring.tsx, 3, 16)) +>P : Symbol(P, Decl(jsxCallbackWithDestructuring.tsx, 1, 20), Decl(jsxCallbackWithDestructuring.tsx, 2, 24)) +>context : Symbol(context, Decl(jsxCallbackWithDestructuring.tsx, 3, 25)) + + render(): {}; +>render : Symbol(Component.render, Decl(jsxCallbackWithDestructuring.tsx, 3, 41)) + + props: Readonly<{ children?: {} }> & Readonly

; +>props : Symbol(Component.props, Decl(jsxCallbackWithDestructuring.tsx, 4, 17)) +>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --)) +>children : Symbol(children, Decl(jsxCallbackWithDestructuring.tsx, 5, 21)) +>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --)) +>P : Symbol(P, Decl(jsxCallbackWithDestructuring.tsx, 1, 20), Decl(jsxCallbackWithDestructuring.tsx, 2, 24)) +} + +declare global { +>global : Symbol(global, Decl(jsxCallbackWithDestructuring.tsx, 6, 1)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(jsxCallbackWithDestructuring.tsx, 8, 16)) + + interface Element { } +>Element : Symbol(Element, Decl(jsxCallbackWithDestructuring.tsx, 9, 19)) + + interface ElementClass { +>ElementClass : Symbol(ElementClass, Decl(jsxCallbackWithDestructuring.tsx, 10, 30)) + + render(): {}; +>render : Symbol(ElementClass.render, Decl(jsxCallbackWithDestructuring.tsx, 11, 33)) + } + interface ElementAttributesProperty { props: {}; } +>ElementAttributesProperty : Symbol(ElementAttributesProperty, Decl(jsxCallbackWithDestructuring.tsx, 13, 9)) +>props : Symbol(ElementAttributesProperty.props, Decl(jsxCallbackWithDestructuring.tsx, 14, 45)) + + interface ElementChildrenAttribute { children: {}; } +>ElementChildrenAttribute : Symbol(ElementChildrenAttribute, Decl(jsxCallbackWithDestructuring.tsx, 14, 58)) +>children : Symbol(ElementChildrenAttribute.children, Decl(jsxCallbackWithDestructuring.tsx, 15, 44)) + + interface IntrinsicAttributes { } +>IntrinsicAttributes : Symbol(IntrinsicAttributes, Decl(jsxCallbackWithDestructuring.tsx, 15, 60)) + + interface IntrinsicClassAttributes { } +>IntrinsicClassAttributes : Symbol(IntrinsicClassAttributes, Decl(jsxCallbackWithDestructuring.tsx, 16, 42)) +>T : Symbol(T, Decl(jsxCallbackWithDestructuring.tsx, 17, 43)) + } +} + +export interface RouteProps { +>RouteProps : Symbol(RouteProps, Decl(jsxCallbackWithDestructuring.tsx, 19, 1)) + + children?: (props: { x: number }) => any; +>children : Symbol(RouteProps.children, Decl(jsxCallbackWithDestructuring.tsx, 21, 29)) +>props : Symbol(props, Decl(jsxCallbackWithDestructuring.tsx, 22, 16)) +>x : Symbol(x, Decl(jsxCallbackWithDestructuring.tsx, 22, 24)) +} +export class MyComponent extends Component { } +>MyComponent : Symbol(MyComponent, Decl(jsxCallbackWithDestructuring.tsx, 23, 1)) +>T : Symbol(T, Decl(jsxCallbackWithDestructuring.tsx, 24, 25)) +>RouteProps : Symbol(RouteProps, Decl(jsxCallbackWithDestructuring.tsx, 19, 1)) +>RouteProps : Symbol(RouteProps, Decl(jsxCallbackWithDestructuring.tsx, 19, 1)) +>Component : Symbol(Component, Decl(jsxCallbackWithDestructuring.tsx, 0, 0), Decl(jsxCallbackWithDestructuring.tsx, 1, 39)) +>T : Symbol(T, Decl(jsxCallbackWithDestructuring.tsx, 24, 25)) + + {}}/> +>MyComponent : Symbol(MyComponent, Decl(jsxCallbackWithDestructuring.tsx, 23, 1)) +>children : Symbol(children, Decl(jsxCallbackWithDestructuring.tsx, 25, 12)) +>x : Symbol(x, Decl(jsxCallbackWithDestructuring.tsx, 25, 25)) + diff --git a/tests/baselines/reference/jsxCallbackWithDestructuring.types b/tests/baselines/reference/jsxCallbackWithDestructuring.types new file mode 100644 index 00000000000..3ee99e72aa3 --- /dev/null +++ b/tests/baselines/reference/jsxCallbackWithDestructuring.types @@ -0,0 +1,83 @@ +=== tests/cases/compiler/jsxCallbackWithDestructuring.tsx === +// minimal component +interface Component

{ } +>Component : Component +>P : P +>S : S + +declare class Component { +>Component : Component +>P : P +>S : S + + constructor(props: P, context?: any); +>props : P +>P : P +>context : any + + render(): {}; +>render : () => {} + + props: Readonly<{ children?: {} }> & Readonly

; +>props : Readonly<{ children?: {} | undefined; }> & Readonly

+>Readonly : Readonly +>children : {} | undefined +>Readonly : Readonly +>P : P +} + +declare global { +>global : any + + namespace JSX { +>JSX : any + + interface Element { } +>Element : Element + + interface ElementClass { +>ElementClass : ElementClass + + render(): {}; +>render : () => {} + } + interface ElementAttributesProperty { props: {}; } +>ElementAttributesProperty : ElementAttributesProperty +>props : {} + + interface ElementChildrenAttribute { children: {}; } +>ElementChildrenAttribute : ElementChildrenAttribute +>children : {} + + interface IntrinsicAttributes { } +>IntrinsicAttributes : IntrinsicAttributes + + interface IntrinsicClassAttributes { } +>IntrinsicClassAttributes : IntrinsicClassAttributes +>T : T + } +} + +export interface RouteProps { +>RouteProps : RouteProps + + children?: (props: { x: number }) => any; +>children : ((props: { x: number; }) => any) | undefined +>props : { x: number; } +>x : number +} +export class MyComponent extends Component { } +>MyComponent : MyComponent +>T : T +>RouteProps : RouteProps +>RouteProps : RouteProps +>Component : Component +>T : T + + {}}/> +> {}}/> : JSX.Element +>MyComponent : typeof MyComponent +>children : ({ x }: { x: number; }) => void +>({ x }) => {} : ({ x }: { x: number; }) => void +>x : number + diff --git a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments4.errors.txt b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments4.errors.txt index 2e502868093..ec6e70cc4a1 100644 --- a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments4.errors.txt +++ b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments4.errors.txt @@ -3,7 +3,7 @@ tests/cases/conformance/jsx/file.tsx(9,33): error TS2322: Type '{ a: number; }' Property 'b' is missing in type '{ a: number; }'. tests/cases/conformance/jsx/file.tsx(10,33): error TS2322: Type 'T & { ignore-prop: true; }' is not assignable to type 'IntrinsicAttributes & { b: {}; a: {}; }'. Type 'T & { ignore-prop: true; }' is not assignable to type '{ b: {}; a: {}; }'. - Property 'a' is missing in type 'T & { ignore-prop: true; }'. + Property 'a' is missing in type '{ b: number; } & { ignore-prop: true; }'. ==== tests/cases/conformance/jsx/file.tsx (2 errors) ==== @@ -24,5 +24,5 @@ tests/cases/conformance/jsx/file.tsx(10,33): error TS2322: Type 'T & { ignore-pr ~~~~~~~~~~~~~~~~~~~~~ !!! error TS2322: Type 'T & { ignore-prop: true; }' is not assignable to type 'IntrinsicAttributes & { b: {}; a: {}; }'. !!! error TS2322: Type 'T & { ignore-prop: true; }' is not assignable to type '{ b: {}; a: {}; }'. -!!! error TS2322: Property 'a' is missing in type 'T & { ignore-prop: true; }'. +!!! error TS2322: Property 'a' is missing in type '{ b: number; } & { ignore-prop: true; }'. } \ No newline at end of file diff --git a/tests/cases/compiler/intersectionOfTypeVariableHasApparentSignatures.ts b/tests/cases/compiler/intersectionOfTypeVariableHasApparentSignatures.ts new file mode 100644 index 00000000000..7bfaaed10c5 --- /dev/null +++ b/tests/cases/compiler/intersectionOfTypeVariableHasApparentSignatures.ts @@ -0,0 +1,17 @@ +// @strictNullChecks: true +// @noImplicitAny: true +interface Component

{ + props: Readonly

& Readonly<{ children?: {} }>; +} + +interface Props { + children?: (items: {x: number}) => void +} + +declare function f(i: Component): void; + +f({ + props: { + children: (({ x }) => { }) + } +}); \ No newline at end of file diff --git a/tests/cases/compiler/jsxCallbackWithDestructuring.tsx b/tests/cases/compiler/jsxCallbackWithDestructuring.tsx new file mode 100644 index 00000000000..c89ff5279c3 --- /dev/null +++ b/tests/cases/compiler/jsxCallbackWithDestructuring.tsx @@ -0,0 +1,31 @@ +// @jsx: preserve +// @noImplicitAny: true +// @strictNullChecks: true + + +// minimal component +interface Component

{ } +declare class Component { + constructor(props: P, context?: any); + render(): {}; + props: Readonly<{ children?: {} }> & Readonly

; +} + +declare global { + namespace JSX { + interface Element { } + interface ElementClass { + render(): {}; + } + interface ElementAttributesProperty { props: {}; } + interface ElementChildrenAttribute { children: {}; } + interface IntrinsicAttributes { } + interface IntrinsicClassAttributes { } + } +} + +export interface RouteProps { + children?: (props: { x: number }) => any; +} +export class MyComponent extends Component { } + {}}/> \ No newline at end of file