Skip to content

Commit

Permalink
Merge pull request #8730 from Microsoft/destructureOptionalParameter
Browse files Browse the repository at this point in the history
Fix destructuring of optional parameters with --strictNullChecks
  • Loading branch information
ahejlsberg committed May 23, 2016
2 parents f1d023a + daafd10 commit 92d465d
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 7 deletions.
14 changes: 7 additions & 7 deletions src/compiler/checker.ts
Expand Up @@ -2745,7 +2745,7 @@ namespace ts {
// assigned by contextual typing.
function getTypeForBindingElementParent(node: VariableLikeDeclaration) {
const symbol = getSymbolOfNode(node);
return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node);
return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false);
}

function getTextOfPropertyName(name: PropertyName): string {
Expand Down Expand Up @@ -2885,7 +2885,7 @@ namespace ts {
}

// Return the inferred type for a variable, parameter, or property declaration
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type {
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type {
if (declaration.flags & NodeFlags.JavaScriptFile) {
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
// one as its type), otherwise fallback to the below standard TS codepaths to
Expand Down Expand Up @@ -2915,7 +2915,7 @@ namespace ts {

// Use type from type annotation if one is present
if (declaration.type) {
return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ !!declaration.questionToken);
return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ declaration.questionToken && includeOptionality);
}

if (declaration.kind === SyntaxKind.Parameter) {
Expand All @@ -2937,13 +2937,13 @@ namespace ts {
? getContextuallyTypedThisType(func)
: getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
if (type) {
return addOptionality(type, /*optional*/ !!declaration.questionToken);
return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality);
}
}

// Use the type of the initializer expression if one is present
if (declaration.initializer) {
return addOptionality(checkExpressionCached(declaration.initializer), /*optional*/ !!declaration.questionToken);
return addOptionality(checkExpressionCached(declaration.initializer), /*optional*/ declaration.questionToken && includeOptionality);
}

// If it is a short-hand property assignment, use the type of the identifier
Expand Down Expand Up @@ -3046,7 +3046,7 @@ namespace ts {
// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
function getWidenedTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, reportErrors?: boolean): Type {
let type = getTypeForVariableLikeDeclaration(declaration);
let type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
if (type) {
if (reportErrors) {
reportErrorsFromWidening(declaration, type);
Expand Down Expand Up @@ -16903,7 +16903,7 @@ namespace ts {
}

if (isBindingPattern(node)) {
return getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>node.parent);
return getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>node.parent, /*includeOptionality*/ true);
}

if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
Expand Down
59 changes: 59 additions & 0 deletions tests/baselines/reference/destructureOptionalParameter.js
@@ -0,0 +1,59 @@
//// [destructureOptionalParameter.ts]

declare function f1({ a, b }?: { a: number, b: string }): void;

function f2({ a, b }: { a: number, b: number } = { a: 0, b: 0 }) {
a;
b;
}

// Repro from #8681

interface Type { t: void }
interface QueryMetadata { q: void }

interface QueryMetadataFactory {
(selector: Type | string, {descendants, read}?: {
descendants?: boolean;
read?: any;
}): ParameterDecorator;
new (selector: Type | string, {descendants, read}?: {
descendants?: boolean;
read?: any;
}): QueryMetadata;
}


//// [destructureOptionalParameter.js]
function f2(_a) {
var _b = _a === void 0 ? { a: 0, b: 0 } : _a, a = _b.a, b = _b.b;
a;
b;
}


//// [destructureOptionalParameter.d.ts]
declare function f1({a, b}?: {
a: number;
b: string;
}): void;
declare function f2({a, b}?: {
a: number;
b: number;
}): void;
interface Type {
t: void;
}
interface QueryMetadata {
q: void;
}
interface QueryMetadataFactory {
(selector: Type | string, {descendants, read}?: {
descendants?: boolean;
read?: any;
}): ParameterDecorator;
new (selector: Type | string, {descendants, read}?: {
descendants?: boolean;
read?: any;
}): QueryMetadata;
}
69 changes: 69 additions & 0 deletions tests/baselines/reference/destructureOptionalParameter.symbols
@@ -0,0 +1,69 @@
=== tests/cases/compiler/destructureOptionalParameter.ts ===

declare function f1({ a, b }?: { a: number, b: string }): void;
>f1 : Symbol(f1, Decl(destructureOptionalParameter.ts, 0, 0))
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 1, 21))
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 1, 24))
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 1, 32))
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 1, 43))

function f2({ a, b }: { a: number, b: number } = { a: 0, b: 0 }) {
>f2 : Symbol(f2, Decl(destructureOptionalParameter.ts, 1, 63))
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 3, 13))
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 3, 16))
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 3, 23))
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 3, 34))
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 3, 50))
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 3, 56))

a;
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 3, 13))

b;
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 3, 16))
}

// Repro from #8681

interface Type { t: void }
>Type : Symbol(Type, Decl(destructureOptionalParameter.ts, 6, 1))
>t : Symbol(Type.t, Decl(destructureOptionalParameter.ts, 10, 16))

interface QueryMetadata { q: void }
>QueryMetadata : Symbol(QueryMetadata, Decl(destructureOptionalParameter.ts, 10, 26))
>q : Symbol(QueryMetadata.q, Decl(destructureOptionalParameter.ts, 11, 25))

interface QueryMetadataFactory {
>QueryMetadataFactory : Symbol(QueryMetadataFactory, Decl(destructureOptionalParameter.ts, 11, 35))

(selector: Type | string, {descendants, read}?: {
>selector : Symbol(selector, Decl(destructureOptionalParameter.ts, 14, 5))
>Type : Symbol(Type, Decl(destructureOptionalParameter.ts, 6, 1))
>descendants : Symbol(descendants, Decl(destructureOptionalParameter.ts, 14, 31))
>read : Symbol(read, Decl(destructureOptionalParameter.ts, 14, 43))

descendants?: boolean;
>descendants : Symbol(descendants, Decl(destructureOptionalParameter.ts, 14, 53))

read?: any;
>read : Symbol(read, Decl(destructureOptionalParameter.ts, 15, 30))

}): ParameterDecorator;
>ParameterDecorator : Symbol(ParameterDecorator, Decl(lib.d.ts, --, --))

new (selector: Type | string, {descendants, read}?: {
>selector : Symbol(selector, Decl(destructureOptionalParameter.ts, 18, 9))
>Type : Symbol(Type, Decl(destructureOptionalParameter.ts, 6, 1))
>descendants : Symbol(descendants, Decl(destructureOptionalParameter.ts, 18, 35))
>read : Symbol(read, Decl(destructureOptionalParameter.ts, 18, 47))

descendants?: boolean;
>descendants : Symbol(descendants, Decl(destructureOptionalParameter.ts, 18, 57))

read?: any;
>read : Symbol(read, Decl(destructureOptionalParameter.ts, 19, 30))

}): QueryMetadata;
>QueryMetadata : Symbol(QueryMetadata, Decl(destructureOptionalParameter.ts, 10, 26))
}

72 changes: 72 additions & 0 deletions tests/baselines/reference/destructureOptionalParameter.types
@@ -0,0 +1,72 @@
=== tests/cases/compiler/destructureOptionalParameter.ts ===

declare function f1({ a, b }?: { a: number, b: string }): void;
>f1 : ({a, b}?: { a: number; b: string; } | undefined) => void
>a : number
>b : string
>a : number
>b : string

function f2({ a, b }: { a: number, b: number } = { a: 0, b: 0 }) {
>f2 : ({a, b}?: { a: number; b: number; }) => void
>a : number
>b : number
>a : number
>b : number
>{ a: 0, b: 0 } : { a: number; b: number; }
>a : number
>0 : number
>b : number
>0 : number

a;
>a : number

b;
>b : number
}

// Repro from #8681

interface Type { t: void }
>Type : Type
>t : void

interface QueryMetadata { q: void }
>QueryMetadata : QueryMetadata
>q : void

interface QueryMetadataFactory {
>QueryMetadataFactory : QueryMetadataFactory

(selector: Type | string, {descendants, read}?: {
>selector : Type | string
>Type : Type
>descendants : boolean | undefined
>read : any

descendants?: boolean;
>descendants : boolean | undefined

read?: any;
>read : any

}): ParameterDecorator;
>ParameterDecorator : (target: Object, propertyKey: string | symbol, parameterIndex: number) => void

new (selector: Type | string, {descendants, read}?: {
>selector : Type | string
>Type : Type
>descendants : boolean | undefined
>read : any

descendants?: boolean;
>descendants : boolean | undefined

read?: any;
>read : any

}): QueryMetadata;
>QueryMetadata : QueryMetadata
}

25 changes: 25 additions & 0 deletions tests/cases/compiler/destructureOptionalParameter.ts
@@ -0,0 +1,25 @@
// @strictNullChecks: true
// @declaration: true

declare function f1({ a, b }?: { a: number, b: string }): void;

function f2({ a, b }: { a: number, b: number } = { a: 0, b: 0 }) {
a;
b;
}

// Repro from #8681

interface Type { t: void }
interface QueryMetadata { q: void }

interface QueryMetadataFactory {
(selector: Type | string, {descendants, read}?: {
descendants?: boolean;
read?: any;
}): ParameterDecorator;
new (selector: Type | string, {descendants, read}?: {
descendants?: boolean;
read?: any;
}): QueryMetadata;
}

0 comments on commit 92d465d

Please sign in to comment.