Skip to content

Commit

Permalink
Reinterpret a type parameter constrained to any as an upper bound con…
Browse files Browse the repository at this point in the history
…straint (#29571)

* Reinterpret a type parameter constrained to any as an upper bound constraint

* Use real constraqint in alias in test

Co-authored-by: Nathan Shively-Sanders <[email protected]>
  • Loading branch information
weswigham and sandersn committed Mar 3, 2020
1 parent 3fdce8e commit 67c6ceb
Show file tree
Hide file tree
Showing 21 changed files with 254 additions and 36 deletions.
14 changes: 12 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11058,8 +11058,18 @@ namespace ts {
}
else {
const constraintDeclaration = getConstraintDeclaration(typeParameter);
typeParameter.constraint = constraintDeclaration ? getTypeFromTypeNode(constraintDeclaration) :
getInferredTypeParameterConstraint(typeParameter) || noConstraintType;
if (!constraintDeclaration) {
typeParameter.constraint = getInferredTypeParameterConstraint(typeParameter) || noConstraintType;
}
else {
let type = getTypeFromTypeNode(constraintDeclaration);
if (type.flags & TypeFlags.Any && type !== errorType) { // Allow errorType to propegate to keep downstream errors suppressed
// use keyofConstraintType as the base constraint for mapped type key constraints (unknown isn;t assignable to that, but `any` was),
// use unknown otherwise
type = constraintDeclaration.parent.parent.kind === SyntaxKind.MappedType ? keyofConstraintType : unknownType;
}
typeParameter.constraint = type;
}
}
}
return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ declare function g1<T>(x: T): T;
>x : T

declare function g2<T extends any>(x: T): T;
>g2 : <T extends any>(x: T) => T
>g2 : <T extends unknown>(x: T) => T
>x : T

declare function g3<T extends unknown>(x: T): T;
Expand Down Expand Up @@ -192,7 +192,7 @@ g1((x = 1) => 0); // number

g2((x = 1) => 0); // number
>g2((x = 1) => 0) : (x?: number) => 0
>g2 : <T extends any>(x: T) => T
>g2 : <T extends unknown>(x: T) => T
>(x = 1) => 0 : (x?: number) => 0
>x : number
>1 : 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
tests/cases/compiler/jsxExcessPropsAndAssignability.tsx(16,6): error TS2322: Type 'ComposedComponentProps & { myProp: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<WrapperComponentProps, any, any>> & Readonly<{ children?: ReactNode; }> & Readonly<WrapperComponentProps>'.
Type 'ComposedComponentProps & { myProp: number; }' is not assignable to type 'Readonly<WrapperComponentProps>'.


==== tests/cases/compiler/jsxExcessPropsAndAssignability.tsx (1 errors) ====
/// <reference path="/.lib/react16.d.ts" />

import * as React from 'react';

const myHoc = <ComposedComponentProps extends any>(
ComposedComponent: React.ComponentClass<ComposedComponentProps>,
) => {
type WrapperComponentProps = ComposedComponentProps & { myProp: string };
const WrapperComponent: React.ComponentClass<WrapperComponentProps> = null as any;

const props: ComposedComponentProps = null as any;

// Expected no error, got none - good
<WrapperComponent {...props} myProp={'1000000'} />;
// Expected error, but got none - bad!
<WrapperComponent {...props} myProp={1000000} />;
~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'ComposedComponentProps & { myProp: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<WrapperComponentProps, any, any>> & Readonly<{ children?: ReactNode; }> & Readonly<WrapperComponentProps>'.
!!! error TS2322: Type 'ComposedComponentProps & { myProp: number; }' is not assignable to type 'Readonly<WrapperComponentProps>'.
};

44 changes: 44 additions & 0 deletions tests/baselines/reference/jsxExcessPropsAndAssignability.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//// [jsxExcessPropsAndAssignability.tsx]
/// <reference path="/.lib/react16.d.ts" />

import * as React from 'react';

const myHoc = <ComposedComponentProps extends any>(
ComposedComponent: React.ComponentClass<ComposedComponentProps>,
) => {
type WrapperComponentProps = ComposedComponentProps & { myProp: string };
const WrapperComponent: React.ComponentClass<WrapperComponentProps> = null as any;

const props: ComposedComponentProps = null as any;

// Expected no error, got none - good
<WrapperComponent {...props} myProp={'1000000'} />;
// Expected error, but got none - bad!
<WrapperComponent {...props} myProp={1000000} />;
};


//// [jsxExcessPropsAndAssignability.js]
"use strict";
/// <reference path="react16.d.ts" />
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
exports.__esModule = true;
var React = require("react");
var myHoc = function (ComposedComponent) {
var WrapperComponent = null;
var props = null;
// Expected no error, got none - good
React.createElement(WrapperComponent, __assign({}, props, { myProp: '1000000' }));
// Expected error, but got none - bad!
React.createElement(WrapperComponent, __assign({}, props, { myProp: 1000000 }));
};
46 changes: 46 additions & 0 deletions tests/baselines/reference/jsxExcessPropsAndAssignability.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
=== tests/cases/compiler/jsxExcessPropsAndAssignability.tsx ===
/// <reference path="react16.d.ts" />

import * as React from 'react';
>React : Symbol(React, Decl(jsxExcessPropsAndAssignability.tsx, 2, 6))

const myHoc = <ComposedComponentProps extends any>(
>myHoc : Symbol(myHoc, Decl(jsxExcessPropsAndAssignability.tsx, 4, 5))
>ComposedComponentProps : Symbol(ComposedComponentProps, Decl(jsxExcessPropsAndAssignability.tsx, 4, 15))

ComposedComponent: React.ComponentClass<ComposedComponentProps>,
>ComposedComponent : Symbol(ComposedComponent, Decl(jsxExcessPropsAndAssignability.tsx, 4, 51))
>React : Symbol(React, Decl(jsxExcessPropsAndAssignability.tsx, 2, 6))
>ComponentClass : Symbol(React.ComponentClass, Decl(react16.d.ts, 421, 9))
>ComposedComponentProps : Symbol(ComposedComponentProps, Decl(jsxExcessPropsAndAssignability.tsx, 4, 15))

) => {
type WrapperComponentProps = ComposedComponentProps & { myProp: string };
>WrapperComponentProps : Symbol(WrapperComponentProps, Decl(jsxExcessPropsAndAssignability.tsx, 6, 6))
>ComposedComponentProps : Symbol(ComposedComponentProps, Decl(jsxExcessPropsAndAssignability.tsx, 4, 15))
>myProp : Symbol(myProp, Decl(jsxExcessPropsAndAssignability.tsx, 7, 59))

const WrapperComponent: React.ComponentClass<WrapperComponentProps> = null as any;
>WrapperComponent : Symbol(WrapperComponent, Decl(jsxExcessPropsAndAssignability.tsx, 8, 9))
>React : Symbol(React, Decl(jsxExcessPropsAndAssignability.tsx, 2, 6))
>ComponentClass : Symbol(React.ComponentClass, Decl(react16.d.ts, 421, 9))
>WrapperComponentProps : Symbol(WrapperComponentProps, Decl(jsxExcessPropsAndAssignability.tsx, 6, 6))

const props: ComposedComponentProps = null as any;
>props : Symbol(props, Decl(jsxExcessPropsAndAssignability.tsx, 10, 9))
>ComposedComponentProps : Symbol(ComposedComponentProps, Decl(jsxExcessPropsAndAssignability.tsx, 4, 15))

// Expected no error, got none - good
<WrapperComponent {...props} myProp={'1000000'} />;
>WrapperComponent : Symbol(WrapperComponent, Decl(jsxExcessPropsAndAssignability.tsx, 8, 9))
>props : Symbol(props, Decl(jsxExcessPropsAndAssignability.tsx, 10, 9))
>myProp : Symbol(myProp, Decl(jsxExcessPropsAndAssignability.tsx, 13, 32))

// Expected error, but got none - bad!
<WrapperComponent {...props} myProp={1000000} />;
>WrapperComponent : Symbol(WrapperComponent, Decl(jsxExcessPropsAndAssignability.tsx, 8, 9))
>props : Symbol(props, Decl(jsxExcessPropsAndAssignability.tsx, 10, 9))
>myProp : Symbol(myProp, Decl(jsxExcessPropsAndAssignability.tsx, 15, 32))

};

48 changes: 48 additions & 0 deletions tests/baselines/reference/jsxExcessPropsAndAssignability.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
=== tests/cases/compiler/jsxExcessPropsAndAssignability.tsx ===
/// <reference path="react16.d.ts" />

import * as React from 'react';
>React : typeof React

const myHoc = <ComposedComponentProps extends any>(
>myHoc : <ComposedComponentProps extends unknown>(ComposedComponent: React.ComponentClass<ComposedComponentProps, any>) => void
><ComposedComponentProps extends any>( ComposedComponent: React.ComponentClass<ComposedComponentProps>,) => { type WrapperComponentProps = ComposedComponentProps & { myProp: string }; const WrapperComponent: React.ComponentClass<WrapperComponentProps> = null as any; const props: ComposedComponentProps = null as any; // Expected no error, got none - good <WrapperComponent {...props} myProp={'1000000'} />; // Expected error, but got none - bad! <WrapperComponent {...props} myProp={1000000} />;} : <ComposedComponentProps extends unknown>(ComposedComponent: React.ComponentClass<ComposedComponentProps, any>) => void

ComposedComponent: React.ComponentClass<ComposedComponentProps>,
>ComposedComponent : React.ComponentClass<ComposedComponentProps, any>
>React : any

) => {
type WrapperComponentProps = ComposedComponentProps & { myProp: string };
>WrapperComponentProps : ComposedComponentProps & { myProp: string; }
>myProp : string

const WrapperComponent: React.ComponentClass<WrapperComponentProps> = null as any;
>WrapperComponent : React.ComponentClass<ComposedComponentProps & { myProp: string; }, any>
>React : any
>null as any : any
>null : null

const props: ComposedComponentProps = null as any;
>props : ComposedComponentProps
>null as any : any
>null : null

// Expected no error, got none - good
<WrapperComponent {...props} myProp={'1000000'} />;
><WrapperComponent {...props} myProp={'1000000'} /> : JSX.Element
>WrapperComponent : React.ComponentClass<ComposedComponentProps & { myProp: string; }, any>
>props : ComposedComponentProps
>myProp : "1000000"
>'1000000' : "1000000"

// Expected error, but got none - bad!
<WrapperComponent {...props} myProp={1000000} />;
><WrapperComponent {...props} myProp={1000000} /> : JSX.Element
>WrapperComponent : React.ComponentClass<ComposedComponentProps & { myProp: string; }, any>
>props : ComposedComponentProps
>myProp : number
>1000000 : 1000000

};

2 changes: 1 addition & 1 deletion tests/baselines/reference/mappedTypeWithAny.types
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ declare let x0: keyof any;
>x0 : string | number | symbol

declare let x1: { [P in any]: Item };
>x1 : { [x: string]: Item; }
>x1 : { [x: string]: Item; [x: number]: Item; }

declare let x2: { [P in string]: Item };
>x2 : { [x: string]: Item; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ x<string, string, string>(null, null, null); // Error

// Generic call with multiple parameters of generic type passed arguments with no best common type
function someGenerics9<T extends any>(a: T, b: T, c: T): T {
>someGenerics9 : <T extends any>(a: T, b: T, c: T) => T
>someGenerics9 : <T extends unknown>(a: T, b: T, c: T) => T
>a : T
>b : T
>c : T
Expand All @@ -368,7 +368,7 @@ function someGenerics9<T extends any>(a: T, b: T, c: T): T {
var a9a = someGenerics9('', 0, []);
>a9a : string
>someGenerics9('', 0, []) : ""
>someGenerics9 : <T extends any>(a: T, b: T, c: T) => T
>someGenerics9 : <T extends unknown>(a: T, b: T, c: T) => T
>'' : ""
>0 : 0
>[] : undefined[]
Expand All @@ -379,7 +379,7 @@ var a9a: {};
var a9b = someGenerics9<{ a?: number; b?: string; }>({ a: 0 }, { b: '' }, null);
>a9b : { a?: number; b?: string; }
>someGenerics9<{ a?: number; b?: string; }>({ a: 0 }, { b: '' }, null) : { a?: number; b?: string; }
>someGenerics9 : <T extends any>(a: T, b: T, c: T) => T
>someGenerics9 : <T extends unknown>(a: T, b: T, c: T) => T
>a : number
>b : string
>{ a: 0 } : { a: number; }
Expand Down Expand Up @@ -413,7 +413,7 @@ interface A92 {
var a9e = someGenerics9(undefined, { x: 6, z: window }, { x: 6, y: '' });
>a9e : { x: number; z: Window & typeof globalThis; y?: undefined; } | { x: number; y: string; z?: undefined; }
>someGenerics9(undefined, { x: 6, z: window }, { x: 6, y: '' }) : { x: number; z: Window & typeof globalThis; y?: undefined; } | { x: number; y: string; z?: undefined; }
>someGenerics9 : <T extends any>(a: T, b: T, c: T) => T
>someGenerics9 : <T extends unknown>(a: T, b: T, c: T) => T
>undefined : undefined
>{ x: 6, z: window } : { x: number; z: Window & typeof globalThis; }
>x : number
Expand All @@ -432,7 +432,7 @@ var a9e: {};
var a9f = someGenerics9<A92>(undefined, { x: 6, z: window }, { x: 6, y: '' });
>a9f : A92
>someGenerics9<A92>(undefined, { x: 6, z: window }, { x: 6, y: '' }) : A92
>someGenerics9 : <T extends any>(a: T, b: T, c: T) => T
>someGenerics9 : <T extends unknown>(a: T, b: T, c: T) => T
>undefined : undefined
>{ x: 6, z: window } : { x: number; z: Window & typeof globalThis; }
>x : number
Expand All @@ -452,7 +452,7 @@ var a9f: A92;
var a9d = someGenerics9({ x: 3 }, { x: 6 }, { x: 6 });
>a9d : { x: number; }
>someGenerics9({ x: 3 }, { x: 6 }, { x: 6 }) : { x: number; }
>someGenerics9 : <T extends any>(a: T, b: T, c: T) => T
>someGenerics9 : <T extends unknown>(a: T, b: T, c: T) => T
>{ x: 3 } : { x: number; }
>x : number
>3 : 3
Expand All @@ -474,7 +474,7 @@ var anyVar: any;
var a = someGenerics9(7, anyVar, 4);
>a : any
>someGenerics9(7, anyVar, 4) : any
>someGenerics9 : <T extends any>(a: T, b: T, c: T) => T
>someGenerics9 : <T extends unknown>(a: T, b: T, c: T) => T
>7 : 7
>anyVar : any
>4 : 4
Expand All @@ -486,7 +486,7 @@ var a: any;
var arr = someGenerics9([], null, undefined);
>arr : any[]
>someGenerics9([], null, undefined) : any[]
>someGenerics9 : <T extends any>(a: T, b: T, c: T) => T
>someGenerics9 : <T extends unknown>(a: T, b: T, c: T) => T
>[] : undefined[]
>null : null
>undefined : undefined
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/typeParameterConstraints1.types
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=== tests/cases/compiler/typeParameterConstraints1.ts ===
function foo1<T extends any>(test: T) { }
>foo1 : <T extends any>(test: T) => void
>foo1 : <T extends unknown>(test: T) => void
>test : T

function foo2<T extends number>(test: T) { }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts(3,7): error TS2339: Property 'blah' does not exist on type 'T'.
tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts(9,7): error TS2339: Property 'blah' does not exist on type 'T'.
tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts(14,7): error TS2339: Property 'children' does not exist on type 'T'.
tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts(15,5): error TS2349: This expression is not callable.
Type '{}' has no call signatures.
tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts(16,9): error TS2351: This expression is not constructable.
Type '{}' has no construct signatures.
tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts(30,14): error TS2339: Property 'children' does not exist on type 'T'.


==== tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts (1 errors) ====
==== tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts (6 errors) ====
function fee<T>() {
var t: T;
t.blah; // Error
Expand All @@ -13,13 +20,23 @@ tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts(3,7): error TS2339: Pr
function fee2<T extends any>() {
var t: T;
t.blah; // ok
~~~~
!!! error TS2339: Property 'blah' does not exist on type 'T'.
t.toString; // ok
}

function f<T extends any>(x: T) {
x.children;
~~~~~~~~
!!! error TS2339: Property 'children' does not exist on type 'T'.
x();
~
!!! error TS2349: This expression is not callable.
!!! error TS2349: Type '{}' has no call signatures.
new x();
~
!!! error TS2351: This expression is not constructable.
!!! error TS2351: Type '{}' has no construct signatures.
x[100];
x['hello'];
}
Expand All @@ -34,6 +51,8 @@ tests/cases/compiler/typeParameterExplicitlyExtendsAny.ts(3,7): error TS2339: Pr
public static displayTree1<T extends Tree<any>>(tree: T) {
// error "Property 'children' does not exist on type 'T'"
tree.children;
~~~~~~~~
!!! error TS2339: Property 'children' does not exist on type 'T'.
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ function fee2<T extends any>() {
>t : Symbol(t, Decl(typeParameterExplicitlyExtendsAny.ts, 7, 7))

t.toString; // ok
>t.toString : Symbol(Object.toString, Decl(lib.es5.d.ts, --, --))
>t : Symbol(t, Decl(typeParameterExplicitlyExtendsAny.ts, 7, 7))
>toString : Symbol(Object.toString, Decl(lib.es5.d.ts, --, --))
}

function f<T extends any>(x: T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function fee<T>() {
}

function fee2<T extends any>() {
>fee2 : <T extends any>() => void
>fee2 : <T extends unknown>() => void

var t: T;
>t : T
Expand All @@ -28,13 +28,13 @@ function fee2<T extends any>() {
>blah : any

t.toString; // ok
>t.toString : any
>t.toString : () => string
>t : T
>toString : any
>toString : () => string
}

function f<T extends any>(x: T) {
>f : <T extends any>(x: T) => void
>f : <T extends unknown>(x: T) => void
>x : T

x.children;
Expand Down Expand Up @@ -74,7 +74,7 @@ class MyClass {
>MyClass : MyClass

public static displayTree1<T extends Tree<any>>(tree: T) {
>displayTree1 : <T extends any>(tree: T) => void
>displayTree1 : <T extends unknown>(tree: T) => void
>tree : T

// error "Property 'children' does not exist on type 'T'"
Expand Down
Loading

0 comments on commit 67c6ceb

Please sign in to comment.