Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix destructuring of optional parameters with --strictNullChecks #8730

Merged
merged 3 commits into from May 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -16877,7 +16877,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;
}