mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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:
+31
-24
@@ -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
|
||||
|
||||
+2
-2
@@ -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)}`;
|
||||
Reference in New Issue
Block a user