Check for keyof constraint type instead of syntactic check (#24098)

* Check for keyof constraint type instead of syntactic check

* Readopt older candidateType check, even though it shouldnt really matter

* OK. Just use maybetypeOfKind

* Remove redundant boolean check
This commit is contained in:
Wesley Wigham 2018-05-22 16:58:31 -07:00 committed by GitHub
parent 8b8cd31784
commit 0102f8050c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 194 additions and 14 deletions

View File

@ -20640,15 +20640,6 @@ namespace ts {
return widened;
}
function isTypeParameterWithKeyofConstraint(type: Type) {
if (type.flags & TypeFlags.TypeParameter) {
const constraintDeclaration = getConstraintDeclaration(<TypeParameter>type);
return constraintDeclaration && constraintDeclaration.kind === SyntaxKind.TypeOperator &&
(<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword;
}
return false;
}
function isLiteralOfContextualType(candidateType: Type, contextualType: Type | undefined): boolean {
if (contextualType) {
if (contextualType.flags & TypeFlags.UnionOrIntersection) {
@ -20660,11 +20651,9 @@ namespace ts {
// this a literal context for literals of that primitive type. For example, given a
// type parameter 'T extends string', infer string literal types for T.
const constraint = getBaseConstraintOfType(contextualType) || emptyObjectType;
return isTypeParameterWithKeyofConstraint(contextualType) && maybeTypeOfKind(candidateType, TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.UniqueESSymbol) ||
constraint.flags & TypeFlags.String && maybeTypeOfKind(candidateType, TypeFlags.StringLiteral) ||
constraint.flags & TypeFlags.Number && maybeTypeOfKind(candidateType, TypeFlags.NumberLiteral) ||
constraint.flags & TypeFlags.Boolean && maybeTypeOfKind(candidateType, TypeFlags.BooleanLiteral) ||
constraint.flags & TypeFlags.ESSymbol && maybeTypeOfKind(candidateType, TypeFlags.UniqueESSymbol) ||
return maybeTypeOfKind(constraint, TypeFlags.String) && maybeTypeOfKind(candidateType, TypeFlags.StringLiteral) ||
maybeTypeOfKind(constraint, TypeFlags.Number) && maybeTypeOfKind(candidateType, TypeFlags.NumberLiteral) ||
maybeTypeOfKind(constraint, TypeFlags.ESSymbol) && maybeTypeOfKind(candidateType, TypeFlags.UniqueESSymbol) ||
isLiteralOfContextualType(candidateType, constraint);
}
// If the contextual type is a literal of a particular primitive type, we consider this a

View File

@ -0,0 +1,34 @@
//// [jsxInferenceProducesLiteralAsExpected.tsx]
import React = require("react");
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
class TestObject {
a: string = '';
b: number = 1;
c: () => void = () => { };
}
interface TestProps<T> {
model: T;
foo: FunctionPropertyNames<T>;
}
function Test<T>(props: TestProps<T>) { return <></>; }
const model = new TestObject();
const el1 = <Test model={model} foo="c" />;
const el2 = <Test<TestObject> model={model} foo="c" />;
//// [jsxInferenceProducesLiteralAsExpected.js]
"use strict";
exports.__esModule = true;
var React = require("react");
var TestObject = /** @class */ (function () {
function TestObject() {
this.a = '';
this.b = 1;
this.c = function () { };
}
return TestObject;
}());
function Test(props) { return React.createElement(React.Fragment, null); }
var model = new TestObject();
var el1 = React.createElement(Test, { model: model, foo: "c" });
var el2 = React.createElement(Test, { model: model, foo: "c" });

View File

@ -0,0 +1,66 @@
=== tests/cases/compiler/jsxInferenceProducesLiteralAsExpected.tsx ===
import React = require("react");
>React : Symbol(React, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 0, 0))
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
>FunctionPropertyNames : Symbol(FunctionPropertyNames, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 0, 32))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 27))
>K : Symbol(K, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 35))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 27))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 27))
>K : Symbol(K, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 35))
>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>K : Symbol(K, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 35))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 27))
class TestObject {
>TestObject : Symbol(TestObject, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 95))
a: string = '';
>a : Symbol(TestObject.a, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 2, 18))
b: number = 1;
>b : Symbol(TestObject.b, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 3, 19))
c: () => void = () => { };
>c : Symbol(TestObject.c, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 4, 18))
}
interface TestProps<T> {
>TestProps : Symbol(TestProps, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 6, 1))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 7, 20))
model: T;
>model : Symbol(TestProps.model, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 7, 24))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 7, 20))
foo: FunctionPropertyNames<T>;
>foo : Symbol(TestProps.foo, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 8, 13))
>FunctionPropertyNames : Symbol(FunctionPropertyNames, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 0, 32))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 7, 20))
}
function Test<T>(props: TestProps<T>) { return <></>; }
>Test : Symbol(Test, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 10, 1))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 11, 14))
>props : Symbol(props, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 11, 17))
>TestProps : Symbol(TestProps, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 6, 1))
>T : Symbol(T, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 11, 14))
const model = new TestObject();
>model : Symbol(model, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 12, 5))
>TestObject : Symbol(TestObject, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 95))
const el1 = <Test model={model} foo="c" />;
>el1 : Symbol(el1, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 14, 5))
>Test : Symbol(Test, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 10, 1))
>model : Symbol(model, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 14, 17))
>model : Symbol(model, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 12, 5))
>foo : Symbol(foo, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 14, 31))
const el2 = <Test<TestObject> model={model} foo="c" />;
>el2 : Symbol(el2, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 15, 5))
>Test : Symbol(Test, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 10, 1))
>TestObject : Symbol(TestObject, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 1, 95))
>model : Symbol(model, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 15, 29))
>model : Symbol(model, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 12, 5))
>foo : Symbol(foo, Decl(jsxInferenceProducesLiteralAsExpected.tsx, 15, 43))

View File

@ -0,0 +1,73 @@
=== tests/cases/compiler/jsxInferenceProducesLiteralAsExpected.tsx ===
import React = require("react");
>React : typeof React
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
>FunctionPropertyNames : { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]
>T : T
>K : K
>T : T
>T : T
>K : K
>Function : Function
>K : K
>T : T
class TestObject {
>TestObject : TestObject
a: string = '';
>a : string
>'' : ""
b: number = 1;
>b : number
>1 : 1
c: () => void = () => { };
>c : () => void
>() => { } : () => void
}
interface TestProps<T> {
>TestProps : TestProps<T>
>T : T
model: T;
>model : T
>T : T
foo: FunctionPropertyNames<T>;
>foo : { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]
>FunctionPropertyNames : { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]
>T : T
}
function Test<T>(props: TestProps<T>) { return <></>; }
>Test : <T>(props: TestProps<T>) => JSX.Element
>T : T
>props : TestProps<T>
>TestProps : TestProps<T>
>T : T
><></> : JSX.Element
const model = new TestObject();
>model : TestObject
>new TestObject() : TestObject
>TestObject : typeof TestObject
const el1 = <Test model={model} foo="c" />;
>el1 : JSX.Element
><Test model={model} foo="c" /> : JSX.Element
>Test : <T>(props: TestProps<T>) => JSX.Element
>model : TestObject
>model : TestObject
>foo : "c"
const el2 = <Test<TestObject> model={model} foo="c" />;
>el2 : JSX.Element
><Test<TestObject> model={model} foo="c" /> : JSX.Element
>Test : <T>(props: TestProps<T>) => JSX.Element
>TestObject : TestObject
>model : TestObject
>model : TestObject
>foo : "c"

View File

@ -0,0 +1,18 @@
// @jsx: react
// @libFiles: lib.d.ts,react.d.ts
import React = require("react");
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
class TestObject {
a: string = '';
b: number = 1;
c: () => void = () => { };
}
interface TestProps<T> {
model: T;
foo: FunctionPropertyNames<T>;
}
function Test<T>(props: TestProps<T>) { return <></>; }
const model = new TestObject();
const el1 = <Test model={model} foo="c" />;
const el2 = <Test<TestObject> model={model} foo="c" />;