Use contextual parameter types over binding pattern initializer types (#28967)

* Use contextual parameter types over binding pattern initializer types

* Remove unneeded check
This commit is contained in:
Wesley Wigham
2018-12-12 15:39:32 -08:00
committed by GitHub
parent fecbdb68bd
commit 5788446aa1
8 changed files with 220 additions and 29 deletions
+31 -24
View File
@@ -4849,7 +4849,7 @@ namespace ts {
if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
}
return declaration.initializer && !getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration)) ?
return declaration.initializer && !getContextualTypeForVariableLikeDeclaration(walkUpBindingElementsAndPatterns(declaration)) ?
getUnionType([type, checkDeclarationInitializer(declaration)], UnionReduction.Subtype) :
type;
}
@@ -17009,6 +17009,32 @@ namespace ts {
}
}
function getContextualTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type | undefined {
const typeNode = getEffectiveTypeAnnotationNode(declaration);
if (typeNode) {
return getTypeFromTypeNode(typeNode);
}
switch (declaration.kind) {
case SyntaxKind.Parameter:
return getContextuallyTypedParameterType(declaration);
case SyntaxKind.BindingElement:
return getContextualTypeForBindingElement(declaration);
// By default, do nothing and return undefined - only parameters and binding elements have context implied by a parent
}
}
function getContextualTypeForBindingElement(declaration: BindingElement): Type | undefined {
const parentDeclaration = declaration.parent.parent;
const name = declaration.propertyName || declaration.name;
const parentType = getContextualTypeForVariableLikeDeclaration(parentDeclaration);
if (parentType && !isBindingPattern(name)) {
const text = getTextOfPropertyName(name);
if (text !== undefined) {
return getTypeOfPropertyOfType(parentType, text);
}
}
}
// In a variable, parameter or property declaration with a type annotation,
// the contextual type of an initializer expression is the type of the variable, parameter or property.
// Otherwise, in a parameter declaration of a contextually typed function expression,
@@ -17020,32 +17046,13 @@ namespace ts {
function getContextualTypeForInitializerExpression(node: Expression): Type | undefined {
const declaration = <VariableLikeDeclaration>node.parent;
if (hasInitializer(declaration) && node === declaration.initializer) {
const typeNode = getEffectiveTypeAnnotationNode(declaration);
if (typeNode) {
return getTypeFromTypeNode(typeNode);
const result = getContextualTypeForVariableLikeDeclaration(declaration);
if (result) {
return result;
}
if (declaration.kind === SyntaxKind.Parameter) {
const type = getContextuallyTypedParameterType(declaration);
if (type) {
return type;
}
}
if (isBindingPattern(declaration.name)) {
if (isBindingPattern(declaration.name)) { // This is less a contextual type and more an implied shape - in some cases, this may be undesirable
return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false);
}
if (isBindingPattern(declaration.parent)) {
const parentDeclaration = declaration.parent.parent;
const name = (declaration as BindingElement).propertyName || declaration.name;
if (parentDeclaration.kind !== SyntaxKind.BindingElement) {
const parentTypeNode = getEffectiveTypeAnnotationNode(parentDeclaration);
if (parentTypeNode && !isBindingPattern(name)) {
const text = getTextOfPropertyName(name);
if (text) {
return getTypeOfPropertyOfType(getTypeFromTypeNode(parentTypeNode), text);
}
}
}
}
}
return undefined;
}
@@ -45,8 +45,8 @@ const [f, g = f, h = i, i = f] = [1]; // error for h = i
>c : number
>d : number
>c : number
>e : any
>e : any
>e : number
>e : number
})([1]);
>[1] : number[]
@@ -0,0 +1,50 @@
//// [destructuringInitializerContextualTypeFromContext.ts]
interface SFC<P = {}> {
(props: P & { children?: any }): any | null;
}
interface Props {
name: "Apollo" | "Artemis" | "Dionysus" | "Persephone";
}
const Parent: SFC<Props> = ({
children,
name = "Artemis",
...props
}) => Child({name, ...props});
const Child: SFC<Props> = ({
children,
name = "Artemis",
...props
}) => `name: ${name} props: ${JSON.stringify(props)}`;
//// [destructuringInitializerContextualTypeFromContext.js]
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);
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
};
var Parent = function (_a) {
var children = _a.children, _b = _a.name, name = _b === void 0 ? "Artemis" : _b, props = __rest(_a, ["children", "name"]);
return Child(__assign({ name: name }, props));
};
var Child = function (_a) {
var children = _a.children, _b = _a.name, name = _b === void 0 ? "Artemis" : _b, props = __rest(_a, ["children", "name"]);
return "name: " + name + " props: " + JSON.stringify(props);
};
@@ -0,0 +1,58 @@
=== tests/cases/compiler/destructuringInitializerContextualTypeFromContext.ts ===
interface SFC<P = {}> {
>SFC : Symbol(SFC, Decl(destructuringInitializerContextualTypeFromContext.ts, 0, 0))
>P : Symbol(P, Decl(destructuringInitializerContextualTypeFromContext.ts, 0, 14))
(props: P & { children?: any }): any | null;
>props : Symbol(props, Decl(destructuringInitializerContextualTypeFromContext.ts, 1, 5))
>P : Symbol(P, Decl(destructuringInitializerContextualTypeFromContext.ts, 0, 14))
>children : Symbol(children, Decl(destructuringInitializerContextualTypeFromContext.ts, 1, 17))
}
interface Props {
>Props : Symbol(Props, Decl(destructuringInitializerContextualTypeFromContext.ts, 2, 1))
name: "Apollo" | "Artemis" | "Dionysus" | "Persephone";
>name : Symbol(Props.name, Decl(destructuringInitializerContextualTypeFromContext.ts, 4, 17))
}
const Parent: SFC<Props> = ({
>Parent : Symbol(Parent, Decl(destructuringInitializerContextualTypeFromContext.ts, 8, 5))
>SFC : Symbol(SFC, Decl(destructuringInitializerContextualTypeFromContext.ts, 0, 0))
>Props : Symbol(Props, Decl(destructuringInitializerContextualTypeFromContext.ts, 2, 1))
children,
>children : Symbol(children, Decl(destructuringInitializerContextualTypeFromContext.ts, 8, 29))
name = "Artemis",
>name : Symbol(name, Decl(destructuringInitializerContextualTypeFromContext.ts, 9, 13))
...props
>props : Symbol(props, Decl(destructuringInitializerContextualTypeFromContext.ts, 10, 21))
}) => Child({name, ...props});
>Child : Symbol(Child, Decl(destructuringInitializerContextualTypeFromContext.ts, 14, 5))
>name : Symbol(name, Decl(destructuringInitializerContextualTypeFromContext.ts, 12, 13))
>props : Symbol(props, Decl(destructuringInitializerContextualTypeFromContext.ts, 10, 21))
const Child: SFC<Props> = ({
>Child : Symbol(Child, Decl(destructuringInitializerContextualTypeFromContext.ts, 14, 5))
>SFC : Symbol(SFC, Decl(destructuringInitializerContextualTypeFromContext.ts, 0, 0))
>Props : Symbol(Props, Decl(destructuringInitializerContextualTypeFromContext.ts, 2, 1))
children,
>children : Symbol(children, Decl(destructuringInitializerContextualTypeFromContext.ts, 14, 28))
name = "Artemis",
>name : Symbol(name, Decl(destructuringInitializerContextualTypeFromContext.ts, 15, 13))
...props
>props : Symbol(props, Decl(destructuringInitializerContextualTypeFromContext.ts, 16, 21))
}) => `name: ${name} props: ${JSON.stringify(props)}`;
>name : Symbol(name, Decl(destructuringInitializerContextualTypeFromContext.ts, 15, 13))
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>props : Symbol(props, Decl(destructuringInitializerContextualTypeFromContext.ts, 16, 21))
@@ -0,0 +1,57 @@
=== tests/cases/compiler/destructuringInitializerContextualTypeFromContext.ts ===
interface SFC<P = {}> {
(props: P & { children?: any }): any | null;
>props : P & { children?: any; }
>children : any
>null : null
}
interface Props {
name: "Apollo" | "Artemis" | "Dionysus" | "Persephone";
>name : "Apollo" | "Artemis" | "Dionysus" | "Persephone"
}
const Parent: SFC<Props> = ({
>Parent : SFC<Props>
>({ children, name = "Artemis", ...props}) => Child({name, ...props}) : ({ children, name, ...props }: Props & { children?: any; }) => any
children,
>children : any
name = "Artemis",
>name : "Apollo" | "Artemis" | "Dionysus" | "Persephone"
>"Artemis" : "Artemis"
...props
>props : {}
}) => Child({name, ...props});
>Child({name, ...props}) : any
>Child : SFC<Props>
>{name, ...props} : { name: "Apollo" | "Artemis" | "Dionysus" | "Persephone"; }
>name : "Apollo" | "Artemis" | "Dionysus" | "Persephone"
>props : {}
const Child: SFC<Props> = ({
>Child : SFC<Props>
>({ children, name = "Artemis", ...props}) => `name: ${name} props: ${JSON.stringify(props)}` : ({ children, name, ...props }: Props & { children?: any; }) => string
children,
>children : any
name = "Artemis",
>name : "Apollo" | "Artemis" | "Dionysus" | "Persephone"
>"Artemis" : "Artemis"
...props
>props : {}
}) => `name: ${name} props: ${JSON.stringify(props)}`;
>`name: ${name} props: ${JSON.stringify(props)}` : string
>name : "Apollo" | "Artemis" | "Dionysus" | "Persephone"
>JSON.stringify(props) : string
>JSON.stringify : { (value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (string | number)[], space?: string | number): string; }
>JSON : JSON
>stringify : { (value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (string | number)[], space?: string | number): string; }
>props : {}
@@ -167,7 +167,7 @@ function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number},
>b : number
>c : number
>6 : 6
>{b: 4, c: 5} : { b: number; c?: number; }
>{b: 4, c: 5} : { b: number; c: number; }
>b : number
>4 : 4
>c : number
@@ -50,7 +50,7 @@ function foo1(
>"secondary" : "secondary"
} = { primary: "SomeSkill", secondary: "someSkill" }
>{ primary: "SomeSkill", secondary: "someSkill" } : { primary?: string; secondary?: string; }
>{ primary: "SomeSkill", secondary: "someSkill" } : { primary: string; secondary: string; }
>primary : string
>"SomeSkill" : "SomeSkill"
>secondary : string
@@ -88,7 +88,7 @@ function foo2(
>"secondary" : "secondary"
} = { primary: "SomeSkill", secondary: "someSkill" }
>{ primary: "SomeSkill", secondary: "someSkill" } : { primary?: string; secondary?: string; }
>{ primary: "SomeSkill", secondary: "someSkill" } : { primary: string; secondary: string; }
>primary : string
>"SomeSkill" : "SomeSkill"
>secondary : string
@@ -0,0 +1,19 @@
interface SFC<P = {}> {
(props: P & { children?: any }): any | null;
}
interface Props {
name: "Apollo" | "Artemis" | "Dionysus" | "Persephone";
}
const Parent: SFC<Props> = ({
children,
name = "Artemis",
...props
}) => Child({name, ...props});
const Child: SFC<Props> = ({
children,
name = "Artemis",
...props
}) => `name: ${name} props: ${JSON.stringify(props)}`;