From 09da3d0bcd21676f2290e51181b3ceafee8ae5ce Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk Date: Tue, 27 Oct 2026 12:06:41 +0200 Subject: [PATCH 1/4] fix(64974): fix contextual typing for destructured parameter defaults --- tsc/internal/checker/checker.go | 5 +- ...ngPatternDefaultMissingElements.errors.txt | 95 ++++++++- ...ndingPatternDefaultMissingElements.symbols | 156 ++++++++++++++ ...BindingPatternDefaultMissingElements.types | 201 ++++++++++++++++++ ...ectBindingPatternDefaultMissingElements.ts | 47 ++++ 5 files changed, 501 insertions(+), 3 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 67a9ab08cfd17..eaa1bde6db78b 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -17219,11 +17219,12 @@ func (c *Checker) checkDeclarationInitializer(declaration *ast.Node, checkMode C t = c.checkExpressionCachedEx(initializer, checkMode) } } - if ast.IsParameterDeclaration(ast.GetRootDeclaration(declaration)) { + root := ast.GetRootDeclaration(declaration) + if ast.IsParameterDeclaration(root) { name := declaration.Name() switch name.Kind { case ast.KindObjectBindingPattern: - if isObjectLiteralType(t) { + if isObjectLiteralType(t) && c.getContextualTypeForVariableLikeDeclaration(root, ContextFlagsNone) == nil { return c.padObjectLiteralType(t, name) } case ast.KindArrayBindingPattern: diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt index 823bd410ef0cb..b041ac978c291 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt @@ -3,6 +3,24 @@ /main.ts(5,5): error TS7031: Binding element 'required' implicitly has an 'any' type. /main.ts(10,5): error TS7031: Binding element 'required' implicitly has an 'any' type. /main.ts(15,5): error TS7031: Binding element 'required' implicitly has an 'any' type. +/main.ts(35,11): error TS2322: Type 'string | undefined' is not assignable to type 'number'. + Type 'undefined' is not assignable to type 'number'. +/main.ts(36,11): error TS2322: Type 'number | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. +/main.ts(41,11): error TS2322: Type 'number | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. +/main.ts(46,11): error TS2322: Type 'number | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. +/main.ts(50,59): error TS2339: Property 'b' does not exist on type '{ a?: number | undefined; }'. +/main.ts(51,11): error TS2322: Type 'number | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. +/main.ts(55,11): error TS2322: Type 'number | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. +/main.ts(59,11): error TS2322: Type 'number' is not assignable to type 'string'. +/main.ts(68,11): error TS2322: Type 'number | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. +/main.ts(72,11): error TS2322: Type 'number | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. ==== /unchecked.js (0 errors) ==== @@ -32,7 +50,7 @@ optional = false, ] = []) => {}; -==== /main.ts (3 errors) ==== +==== /main.ts (13 errors) ==== import { checkedArray, checkedObject } from "./checked"; import { uncheckedArray, uncheckedObject } from "./unchecked"; @@ -65,4 +83,79 @@ checkedArray(["value"]); typedObject({ required: "value" }); typedArray(["value"]); + + type F = (input?: { a?: string; b?: number }) => number; + export const f1: F = ({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0); + + export const f2: (input: { a?: string; b?: number }) => number = ({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0); + + declare function f3(callback: F): void; + f3(({ a, b } = {}) => { + const c: number = a; + ~ +!!! error TS2322: Type 'string | undefined' is not assignable to type 'number'. +!!! error TS2322: Type 'undefined' is not assignable to type 'number'. + const d: string = b; + ~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + return 0; + }); + + export const f4: (input?: { a?: { b?: number } }) => number = ({ a: { b } = {} } = {}) => { + const c: string = b; + ~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + return b ?? 0; + }; + + export function f5({ a: { b } = {} }: { a?: { b?: number } }) { + const c: string = b; + ~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + return b; + } + + export const f6: (input?: { a?: number }) => void = ({ a, b } = {}) => { + ~ +!!! error TS2339: Property 'b' does not exist on type '{ a?: number | undefined; }'. + const c: string = a; + ~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + }; + + export const f7: (input?: [{ a?: number }?]) => void = ([{ a } = {}] = []) => { + const b: string = a; + ~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + }; + + export const f8: (input?: { [key: string]: number }) => void = ({ a } = {}) => { + const b: string = a; + ~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + }; + + export const f9: (input?: any) => void = ({ a: { b } = {} } = {}) => { + b; + }; + + declare function f10(value: T, callback: (input?: Partial) => void): void; + f10({ a: 1 }, ({ a } = {}) => { + const b: string = a; + ~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + }); + + export const f11: (input?: { a?: number }[]) => void = ([...[{ a } = {}]] = []) => { + const b: string = a; + ~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + }; \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols index 5542ea8d095bf..e088f2174ec64 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols @@ -114,3 +114,159 @@ typedObject({ required: "value" }); typedArray(["value"]); >typedArray : Symbol(typedArray, Decl(main.ts, 8, 12)) +type F = (input?: { a?: string; b?: number }) => number; +>F : Symbol(F, Decl(main.ts, 25, 22)) +>input : Symbol(input, Decl(main.ts, 27, 10)) +>a : Symbol(a, Decl(main.ts, 27, 19)) +>b : Symbol(b, Decl(main.ts, 27, 31)) + +export const f1: F = ({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0); +>f1 : Symbol(f1, Decl(main.ts, 28, 12)) +>F : Symbol(F, Decl(main.ts, 25, 22)) +>a : Symbol(a, Decl(main.ts, 28, 23)) +>b : Symbol(b, Decl(main.ts, 28, 26)) +>a : Symbol(a, Decl(main.ts, 28, 23)) +>b : Symbol(b, Decl(main.ts, 28, 26)) + +export const f2: (input: { a?: string; b?: number }) => number = ({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0); +>f2 : Symbol(f2, Decl(main.ts, 30, 12)) +>input : Symbol(input, Decl(main.ts, 30, 18)) +>a : Symbol(a, Decl(main.ts, 30, 26)) +>b : Symbol(b, Decl(main.ts, 30, 38)) +>a : Symbol(a, Decl(main.ts, 30, 67)) +>b : Symbol(b, Decl(main.ts, 30, 70)) +>a : Symbol(a, Decl(main.ts, 30, 67)) +>b : Symbol(b, Decl(main.ts, 30, 70)) + +declare function f3(callback: F): void; +>f3 : Symbol(f3, Decl(main.ts, 30, 107)) +>callback : Symbol(callback, Decl(main.ts, 32, 20)) +>F : Symbol(F, Decl(main.ts, 25, 22)) + +f3(({ a, b } = {}) => { +>f3 : Symbol(f3, Decl(main.ts, 30, 107)) +>a : Symbol(a, Decl(main.ts, 33, 5)) +>b : Symbol(b, Decl(main.ts, 33, 8)) + + const c: number = a; +>c : Symbol(c, Decl(main.ts, 34, 9)) +>a : Symbol(a, Decl(main.ts, 33, 5)) + + const d: string = b; +>d : Symbol(d, Decl(main.ts, 35, 9)) +>b : Symbol(b, Decl(main.ts, 33, 8)) + + return 0; +}); + +export const f4: (input?: { a?: { b?: number } }) => number = ({ a: { b } = {} } = {}) => { +>f4 : Symbol(f4, Decl(main.ts, 39, 12)) +>input : Symbol(input, Decl(main.ts, 39, 18)) +>a : Symbol(a, Decl(main.ts, 39, 27)) +>b : Symbol(b, Decl(main.ts, 39, 33)) +>b : Symbol(b, Decl(main.ts, 39, 69)) + + const c: string = b; +>c : Symbol(c, Decl(main.ts, 40, 9)) +>b : Symbol(b, Decl(main.ts, 39, 69)) + + return b ?? 0; +>b : Symbol(b, Decl(main.ts, 39, 69)) + +}; + +export function f5({ a: { b } = {} }: { a?: { b?: number } }) { +>f5 : Symbol(f5, Decl(main.ts, 42, 2)) +>a : Symbol(a, Decl(main.ts, 44, 39)) +>b : Symbol(b, Decl(main.ts, 44, 25)) +>a : Symbol(a, Decl(main.ts, 44, 39)) +>b : Symbol(b, Decl(main.ts, 44, 45)) + + const c: string = b; +>c : Symbol(c, Decl(main.ts, 45, 9)) +>b : Symbol(b, Decl(main.ts, 44, 25)) + + return b; +>b : Symbol(b, Decl(main.ts, 44, 25)) +} + +export const f6: (input?: { a?: number }) => void = ({ a, b } = {}) => { +>f6 : Symbol(f6, Decl(main.ts, 49, 12)) +>input : Symbol(input, Decl(main.ts, 49, 18)) +>a : Symbol(a, Decl(main.ts, 49, 27)) +>a : Symbol(a, Decl(main.ts, 49, 54)) +>b : Symbol(b, Decl(main.ts, 49, 57)) + + const c: string = a; +>c : Symbol(c, Decl(main.ts, 50, 9)) +>a : Symbol(a, Decl(main.ts, 49, 54)) + +}; + +export const f7: (input?: [{ a?: number }?]) => void = ([{ a } = {}] = []) => { +>f7 : Symbol(f7, Decl(main.ts, 53, 12)) +>input : Symbol(input, Decl(main.ts, 53, 18)) +>a : Symbol(a, Decl(main.ts, 53, 28)) +>a : Symbol(a, Decl(main.ts, 53, 58)) + + const b: string = a; +>b : Symbol(b, Decl(main.ts, 54, 9)) +>a : Symbol(a, Decl(main.ts, 53, 58)) + +}; + +export const f8: (input?: { [key: string]: number }) => void = ({ a } = {}) => { +>f8 : Symbol(f8, Decl(main.ts, 57, 12)) +>input : Symbol(input, Decl(main.ts, 57, 18)) +>key : Symbol(key, Decl(main.ts, 57, 29)) +>a : Symbol(a, Decl(main.ts, 57, 65)) + + const b: string = a; +>b : Symbol(b, Decl(main.ts, 58, 9)) +>a : Symbol(a, Decl(main.ts, 57, 65)) + +}; + +export const f9: (input?: any) => void = ({ a: { b } = {} } = {}) => { +>f9 : Symbol(f9, Decl(main.ts, 61, 12)) +>input : Symbol(input, Decl(main.ts, 61, 18)) +>b : Symbol(b, Decl(main.ts, 61, 48)) + + b; +>b : Symbol(b, Decl(main.ts, 61, 48)) + +}; + +declare function f10(value: T, callback: (input?: Partial) => void): void; +>f10 : Symbol(f10, Decl(main.ts, 63, 2)) +>T : Symbol(T, Decl(main.ts, 65, 21)) +>value : Symbol(value, Decl(main.ts, 65, 24)) +>T : Symbol(T, Decl(main.ts, 65, 21)) +>callback : Symbol(callback, Decl(main.ts, 65, 33)) +>input : Symbol(input, Decl(main.ts, 65, 45)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(main.ts, 65, 21)) + +f10({ a: 1 }, ({ a } = {}) => { +>f10 : Symbol(f10, Decl(main.ts, 63, 2)) +>a : Symbol(a, Decl(main.ts, 66, 5)) +>a : Symbol(a, Decl(main.ts, 66, 16)) + + const b: string = a; +>b : Symbol(b, Decl(main.ts, 67, 9)) +>a : Symbol(a, Decl(main.ts, 66, 16)) + +}); + +export const f11: (input?: { a?: number }[]) => void = ([...[{ a } = {}]] = []) => { +>f11 : Symbol(f11, Decl(main.ts, 70, 12)) +>input : Symbol(input, Decl(main.ts, 70, 19)) +>a : Symbol(a, Decl(main.ts, 70, 28)) +>a : Symbol(a, Decl(main.ts, 70, 62)) + + const b: string = a; +>b : Symbol(b, Decl(main.ts, 71, 9)) +>a : Symbol(a, Decl(main.ts, 70, 62)) + +}; + diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types index 1791cbfaebf14..0b11169868215 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types @@ -153,3 +153,204 @@ typedArray(["value"]); >["value"] : [string] >"value" : "value" +type F = (input?: { a?: string; b?: number }) => number; +>F : F +>input : { a?: string; b?: number; } | undefined +>a : string | undefined +>b : number | undefined + +export const f1: F = ({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0); +>f1 : F +>({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0) : ({ a, b }?: { a?: string; b?: number; } | undefined) => number +>a : string | undefined +>b : number | undefined +>{} : {} +>(a ? 1 : 0) + (b ?? 0) : number +>(a ? 1 : 0) : 0 | 1 +>a ? 1 : 0 : 0 | 1 +>a : string | undefined +>1 : 1 +>0 : 0 +>(b ?? 0) : number +>b ?? 0 : number +>b : number | undefined +>0 : 0 + +export const f2: (input: { a?: string; b?: number }) => number = ({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0); +>f2 : (input: { a?: string; b?: number; }) => number +>input : { a?: string; b?: number; } +>a : string | undefined +>b : number | undefined +>({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0) : ({ a, b }?: { a?: string; b?: number; }) => number +>a : string | undefined +>b : number | undefined +>{} : {} +>(a ? 1 : 0) + (b ?? 0) : number +>(a ? 1 : 0) : 0 | 1 +>a ? 1 : 0 : 0 | 1 +>a : string | undefined +>1 : 1 +>0 : 0 +>(b ?? 0) : number +>b ?? 0 : number +>b : number | undefined +>0 : 0 + +declare function f3(callback: F): void; +>f3 : (callback: F) => void +>callback : F + +f3(({ a, b } = {}) => { +>f3(({ a, b } = {}) => { const c: number = a; const d: string = b; return 0;}) : void +>f3 : (callback: F) => void +>({ a, b } = {}) => { const c: number = a; const d: string = b; return 0;} : ({ a, b }?: { a?: string; b?: number; } | undefined) => number +>a : string | undefined +>b : number | undefined +>{} : {} + + const c: number = a; +>c : number +>a : string | undefined + + const d: string = b; +>d : string +>b : number | undefined + + return 0; +>0 : 0 + +}); + +export const f4: (input?: { a?: { b?: number } }) => number = ({ a: { b } = {} } = {}) => { +>f4 : (input?: { a?: { b?: number; }; }) => number +>input : { a?: { b?: number; }; } | undefined +>a : { b?: number; } | undefined +>b : number | undefined +>({ a: { b } = {} } = {}) => { const c: string = b; return b ?? 0;} : ({ a: { b } }?: { a?: { b?: number; }; } | undefined) => number +>a : any +>b : number | undefined +>{} : {} +>{} : {} + + const c: string = b; +>c : string +>b : number | undefined + + return b ?? 0; +>b ?? 0 : number +>b : number | undefined +>0 : 0 + +}; + +export function f5({ a: { b } = {} }: { a?: { b?: number } }) { +>f5 : ({ a: { b } }: { a?: { b?: number; }; }) => number | undefined +>a : any +>b : number | undefined +>{} : {} +>a : { b?: number; } | undefined +>b : number | undefined + + const c: string = b; +>c : string +>b : number | undefined + + return b; +>b : number | undefined +} + +export const f6: (input?: { a?: number }) => void = ({ a, b } = {}) => { +>f6 : (input?: { a?: number; }) => void +>input : { a?: number; } | undefined +>a : number | undefined +>({ a, b } = {}) => { const c: string = a;} : ({ a, b }?: { a?: number; } | undefined) => void +>a : number | undefined +>b : any +>{} : {} + + const c: string = a; +>c : string +>a : number | undefined + +}; + +export const f7: (input?: [{ a?: number }?]) => void = ([{ a } = {}] = []) => { +>f7 : (input?: [{ a?: number; }?]) => void +>input : [({ a?: number; } | undefined)?] | undefined +>a : number | undefined +>([{ a } = {}] = []) => { const b: string = a;} : ([{ a }]?: [({ a?: number; } | undefined)?] | undefined) => void +>a : number | undefined +>{} : {} +>[] : [] + + const b: string = a; +>b : string +>a : number | undefined + +}; + +export const f8: (input?: { [key: string]: number }) => void = ({ a } = {}) => { +>f8 : (input?: { [key: string]: number; }) => void +>input : { [key: string]: number; } | undefined +>key : string +>({ a } = {}) => { const b: string = a;} : ({ a }?: { [key: string]: number; } | undefined) => void +>a : number +>{} : {} + + const b: string = a; +>b : string +>a : number + +}; + +export const f9: (input?: any) => void = ({ a: { b } = {} } = {}) => { +>f9 : (input?: any) => void +>input : any +>({ a: { b } = {} } = {}) => { b;} : ({ a: { b } }?: any) => void +>a : any +>b : any +>{} : {} +>{} : {} + + b; +>b : any + +}; + +declare function f10(value: T, callback: (input?: Partial) => void): void; +>f10 : (value: T, callback: (input?: Partial) => void) => void +>value : T +>callback : (input?: Partial) => void +>input : Partial | undefined + +f10({ a: 1 }, ({ a } = {}) => { +>f10({ a: 1 }, ({ a } = {}) => { const b: string = a;}) : void +>f10 : (value: T, callback: (input?: Partial) => void) => void +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>({ a } = {}) => { const b: string = a;} : ({ a }?: Partial<{ a: number; }> | undefined) => void +>a : number | undefined +>{} : {} + + const b: string = a; +>b : string +>a : number | undefined + +}); + +export const f11: (input?: { a?: number }[]) => void = ([...[{ a } = {}]] = []) => { +>f11 : (input?: { a?: number; }[]) => void +>input : { a?: number; }[] | undefined +>a : number | undefined +>([...[{ a } = {}]] = []) => { const b: string = a;} : ([...[{ a }]]?: { a?: number; }[] | undefined) => void +>a : number | undefined +>{} : {} +>[] : never[] + + const b: string = a; +>b : string +>a : number | undefined + +}; + diff --git a/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts b/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts index e0ea93281bfc9..cc1004369b766 100644 --- a/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts +++ b/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts @@ -53,3 +53,50 @@ checkedObject({ required: "value" }); checkedArray(["value"]); typedObject({ required: "value" }); typedArray(["value"]); + +type F = (input?: { a?: string; b?: number }) => number; +export const f1: F = ({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0); + +export const f2: (input: { a?: string; b?: number }) => number = ({ a, b } = {}) => (a ? 1 : 0) + (b ?? 0); + +declare function f3(callback: F): void; +f3(({ a, b } = {}) => { + const c: number = a; + const d: string = b; + return 0; +}); + +export const f4: (input?: { a?: { b?: number } }) => number = ({ a: { b } = {} } = {}) => { + const c: string = b; + return b ?? 0; +}; + +export function f5({ a: { b } = {} }: { a?: { b?: number } }) { + const c: string = b; + return b; +} + +export const f6: (input?: { a?: number }) => void = ({ a, b } = {}) => { + const c: string = a; +}; + +export const f7: (input?: [{ a?: number }?]) => void = ([{ a } = {}] = []) => { + const b: string = a; +}; + +export const f8: (input?: { [key: string]: number }) => void = ({ a } = {}) => { + const b: string = a; +}; + +export const f9: (input?: any) => void = ({ a: { b } = {} } = {}) => { + b; +}; + +declare function f10(value: T, callback: (input?: Partial) => void): void; +f10({ a: 1 }, ({ a } = {}) => { + const b: string = a; +}); + +export const f11: (input?: { a?: number }[]) => void = ([...[{ a } = {}]] = []) => { + const b: string = a; +}; From 667c4f22010895da2907e930ee6ecbdcbb50fa36 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk Date: Tue, 27 Oct 2026 21:22:41 +0200 Subject: [PATCH 2/4] add additional tests --- ...ngPatternDefaultMissingElements.errors.txt | 4 +++ ...ndingPatternDefaultMissingElements.symbols | 24 ++++++++++++++ ...BindingPatternDefaultMissingElements.types | 33 +++++++++++++++++++ ...ectBindingPatternDefaultMissingElements.ts | 4 +++ 4 files changed, 65 insertions(+) diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt index b041ac978c291..8b17a30ccfe0a 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt @@ -158,4 +158,8 @@ !!! error TS2322: Type 'number | undefined' is not assignable to type 'string'. !!! error TS2322: Type 'undefined' is not assignable to type 'string'. }; + + export const f12: (input: { a: boolean; b?: string }) => string = ({ a, b } = { a: false }) => `${a}${b}`; + + export const f13 = (input: { a?: number; b?: string }[]) => input.map(({ a, b } = {}) => `${a}${b}`); \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols index e088f2174ec64..6e3cf3d45c050 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols @@ -270,3 +270,27 @@ export const f11: (input?: { a?: number }[]) => void = ([...[{ a } = {}]] = []) }; +export const f12: (input: { a: boolean; b?: string }) => string = ({ a, b } = { a: false }) => `${a}${b}`; +>f12 : Symbol(f12, Decl(main.ts, 74, 12)) +>input : Symbol(input, Decl(main.ts, 74, 19)) +>a : Symbol(a, Decl(main.ts, 74, 27)) +>b : Symbol(b, Decl(main.ts, 74, 39)) +>a : Symbol(a, Decl(main.ts, 74, 68)) +>b : Symbol(b, Decl(main.ts, 74, 71)) +>a : Symbol(a, Decl(main.ts, 74, 79)) +>a : Symbol(a, Decl(main.ts, 74, 68)) +>b : Symbol(b, Decl(main.ts, 74, 71)) + +export const f13 = (input: { a?: number; b?: string }[]) => input.map(({ a, b } = {}) => `${a}${b}`); +>f13 : Symbol(f13, Decl(main.ts, 76, 12)) +>input : Symbol(input, Decl(main.ts, 76, 20)) +>a : Symbol(a, Decl(main.ts, 76, 28)) +>b : Symbol(b, Decl(main.ts, 76, 40)) +>input.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>input : Symbol(input, Decl(main.ts, 76, 20)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(main.ts, 76, 72)) +>b : Symbol(b, Decl(main.ts, 76, 75)) +>a : Symbol(a, Decl(main.ts, 76, 72)) +>b : Symbol(b, Decl(main.ts, 76, 75)) + diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types index 0b11169868215..223d002095b2f 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types @@ -354,3 +354,36 @@ export const f11: (input?: { a?: number }[]) => void = ([...[{ a } = {}]] = []) }; +export const f12: (input: { a: boolean; b?: string }) => string = ({ a, b } = { a: false }) => `${a}${b}`; +>f12 : (input: { a: boolean; b?: string; }) => string +>input : { a: boolean; b?: string; } +>a : boolean +>b : string | undefined +>({ a, b } = { a: false }) => `${a}${b}` : ({ a, b }?: { a: boolean; b?: string; }) => string +>a : boolean +>b : string | undefined +>{ a: false } : { a: false; } +>a : false +>false : false +>`${a}${b}` : string +>a : boolean +>b : string | undefined + +export const f13 = (input: { a?: number; b?: string }[]) => input.map(({ a, b } = {}) => `${a}${b}`); +>f13 : (input: { a?: number; b?: string; }[]) => string[] +>(input: { a?: number; b?: string }[]) => input.map(({ a, b } = {}) => `${a}${b}`) : (input: { a?: number; b?: string; }[]) => string[] +>input : { a?: number; b?: string; }[] +>a : number | undefined +>b : string | undefined +>input.map(({ a, b } = {}) => `${a}${b}`) : string[] +>input.map : (callbackfn: (value: { a?: number; b?: string; }, index: number, array: { a?: number; b?: string; }[]) => U, thisArg?: any) => U[] +>input : { a?: number; b?: string; }[] +>map : (callbackfn: (value: { a?: number; b?: string; }, index: number, array: { a?: number; b?: string; }[]) => U, thisArg?: any) => U[] +>({ a, b } = {}) => `${a}${b}` : ({ a, b }?: { a?: number; b?: string; }) => string +>a : number | undefined +>b : string | undefined +>{} : {} +>`${a}${b}` : string +>a : number | undefined +>b : string | undefined + diff --git a/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts b/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts index cc1004369b766..bc71002647daf 100644 --- a/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts +++ b/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts @@ -100,3 +100,7 @@ f10({ a: 1 }, ({ a } = {}) => { export const f11: (input?: { a?: number }[]) => void = ([...[{ a } = {}]] = []) => { const b: string = a; }; + +export const f12: (input: { a: boolean; b?: string }) => string = ({ a, b } = { a: false }) => `${a}${b}`; + +export const f13 = (input: { a?: number; b?: string }[]) => input.map(({ a, b } = {}) => `${a}${b}`); From e0fce9a0cb17aaa809aa7ea39807d3d74b64b52c Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk Date: Fri, 30 Oct 2026 12:08:11 +0200 Subject: [PATCH 3/4] add additional tests --- ...ngPatternDefaultMissingElements.errors.txt | 5 ++++ ...ndingPatternDefaultMissingElements.symbols | 20 ++++++++++++++++ ...BindingPatternDefaultMissingElements.types | 23 +++++++++++++++++++ ...ectBindingPatternDefaultMissingElements.ts | 5 ++++ 4 files changed, 53 insertions(+) diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt index 8b17a30ccfe0a..1d4aa13b980d6 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt @@ -162,4 +162,9 @@ export const f12: (input: { a: boolean; b?: string }) => string = ({ a, b } = { a: false }) => `${a}${b}`; export const f13 = (input: { a?: number; b?: string }[]) => input.map(({ a, b } = {}) => `${a}${b}`); + + export const f14: (args: string[], input?: { a?: string; b?: number }) => void = (args, { a, b = 1 } = {}) => { + const c: string | undefined = a; + const d: number = b; + }; \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols index 6e3cf3d45c050..084f320ac8d58 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols @@ -294,3 +294,23 @@ export const f13 = (input: { a?: number; b?: string }[]) => input.map(({ a, b } >a : Symbol(a, Decl(main.ts, 76, 72)) >b : Symbol(b, Decl(main.ts, 76, 75)) +export const f14: (args: string[], input?: { a?: string; b?: number }) => void = (args, { a, b = 1 } = {}) => { +>f14 : Symbol(f14, Decl(main.ts, 78, 12)) +>args : Symbol(args, Decl(main.ts, 78, 19)) +>input : Symbol(input, Decl(main.ts, 78, 34)) +>a : Symbol(a, Decl(main.ts, 78, 44)) +>b : Symbol(b, Decl(main.ts, 78, 56)) +>args : Symbol(args, Decl(main.ts, 78, 82)) +>a : Symbol(a, Decl(main.ts, 78, 89)) +>b : Symbol(b, Decl(main.ts, 78, 92)) + + const c: string | undefined = a; +>c : Symbol(c, Decl(main.ts, 79, 9)) +>a : Symbol(a, Decl(main.ts, 78, 89)) + + const d: number = b; +>d : Symbol(d, Decl(main.ts, 80, 9)) +>b : Symbol(b, Decl(main.ts, 78, 92)) + +}; + diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types index 223d002095b2f..ba0319302ea4e 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types @@ -387,3 +387,26 @@ export const f13 = (input: { a?: number; b?: string }[]) => input.map(({ a, b } >a : number | undefined >b : string | undefined +export const f14: (args: string[], input?: { a?: string; b?: number }) => void = (args, { a, b = 1 } = {}) => { +>f14 : (args: string[], input?: { a?: string; b?: number; }) => void +>args : string[] +>input : { a?: string; b?: number; } | undefined +>a : string | undefined +>b : number | undefined +>(args, { a, b = 1 } = {}) => { const c: string | undefined = a; const d: number = b;} : (args: string[], { a, b }?: { a?: string; b?: number; } | undefined) => void +>args : string[] +>a : string | undefined +>b : number +>1 : 1 +>{} : {} + + const c: string | undefined = a; +>c : string | undefined +>a : string | undefined + + const d: number = b; +>d : number +>b : number + +}; + diff --git a/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts b/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts index bc71002647daf..7ba77677d6433 100644 --- a/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts +++ b/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts @@ -104,3 +104,8 @@ export const f11: (input?: { a?: number }[]) => void = ([...[{ a } = {}]] = []) export const f12: (input: { a: boolean; b?: string }) => string = ({ a, b } = { a: false }) => `${a}${b}`; export const f13 = (input: { a?: number; b?: string }[]) => input.map(({ a, b } = {}) => `${a}${b}`); + +export const f14: (args: string[], input?: { a?: string; b?: number }) => void = (args, { a, b = 1 } = {}) => { + const c: string | undefined = a; + const d: number = b; +}; From 7981eae82bc17bce66fc146885eaf86e7a0919d8 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk Date: Fri, 30 Oct 2026 13:05:24 +0200 Subject: [PATCH 4/4] add jsdoc tests --- ...ngPatternDefaultMissingElements.errors.txt | 17 +++++++ ...ndingPatternDefaultMissingElements.symbols | 37 +++++++++++++++ ...BindingPatternDefaultMissingElements.types | 46 +++++++++++++++++++ ...ectBindingPatternDefaultMissingElements.ts | 18 ++++++++ 4 files changed, 118 insertions(+) diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt index 1d4aa13b980d6..4230d9551dee8 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.errors.txt @@ -50,6 +50,23 @@ optional = false, ] = []) => {}; + /** @typedef {{ a?: number }} A */ + /** @typedef {{ f: (b: string, c?: A) => Promise }} B */ + /** @returns {B} */ + export function f15() { + return { + async f(b, { a } = {}) { + a?.toFixed(); + return [b]; + }, + }; + } + + /** @typedef {(b: number, c?: A) => Promise} F */ + export const f16 = /** @type {F} */ (async (b, { a } = {}) => { + a?.toFixed(); + }); + ==== /main.ts (13 errors) ==== import { checkedArray, checkedObject } from "./checked"; import { uncheckedArray, uncheckedObject } from "./unchecked"; diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols index 084f320ac8d58..3310eef0a67af 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.symbols @@ -47,6 +47,43 @@ export const checkedArray = ([ ] = []) => {}; +/** @typedef {{ a?: number }} A */ +/** @typedef {{ f: (b: string, c?: A) => Promise }} B */ +/** @returns {B} */ +export function f15() { +>f15 : Symbol(f15, Decl(checked.js, 9, 14)) + + return { + async f(b, { a } = {}) { +>f : Symbol(f, Decl(checked.js, 15, 12)) +>b : Symbol(b, Decl(checked.js, 16, 16)) +>a : Symbol(a, Decl(checked.js, 16, 20)) + + a?.toFixed(); +>a?.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(checked.js, 16, 20)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + + return [b]; +>b : Symbol(b, Decl(checked.js, 16, 16)) + + }, + }; +} + +/** @typedef {(b: number, c?: A) => Promise} F */ +export const f16 = /** @type {F} */ (async (b, { a } = {}) => { +>f16 : Symbol(f16, Decl(checked.js, 24, 12)) +>b : Symbol(b, Decl(checked.js, 24, 44)) +>a : Symbol(a, Decl(checked.js, 24, 48)) + + a?.toFixed(); +>a?.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(checked.js, 24, 48)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + +}); + === /main.ts === import { checkedArray, checkedObject } from "./checked"; >checkedArray : Symbol(checkedArray, Decl(main.ts, 0, 8)) diff --git a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types index ba0319302ea4e..ab099f217ea98 100644 --- a/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types +++ b/tsc/testdata/baselines/reference/compiler/objectBindingPatternDefaultMissingElements.types @@ -59,6 +59,52 @@ export const checkedArray = ([ ] = []) => {}; >[] : [] +/** @typedef {{ a?: number }} A */ +/** @typedef {{ f: (b: string, c?: A) => Promise }} B */ +/** @returns {B} */ +export function f15() { +>f15 : () => B + + return { +>{ async f(b, { a } = {}) { a?.toFixed(); return [b]; }, } : { f(b: string, { a }?: A | undefined): Promise; } + + async f(b, { a } = {}) { +>f : (b: string, { a }?: A | undefined) => Promise +>b : string +>a : number | undefined +>{} : {} + + a?.toFixed(); +>a?.toFixed() : string | undefined +>a?.toFixed : ((fractionDigits?: number) => string) | undefined +>a : number | undefined +>toFixed : ((fractionDigits?: number) => string) | undefined + + return [b]; +>[b] : string[] +>b : string + + }, + }; +} + +/** @typedef {(b: number, c?: A) => Promise} F */ +export const f16 = /** @type {F} */ (async (b, { a } = {}) => { +>f16 : F +>(async (b, { a } = {}) => { a?.toFixed();}) : F +>async (b, { a } = {}) => { a?.toFixed();} : (b: number, { a }?: A | undefined) => Promise +>b : number +>a : number | undefined +>{} : {} + + a?.toFixed(); +>a?.toFixed() : string | undefined +>a?.toFixed : ((fractionDigits?: number) => string) | undefined +>a : number | undefined +>toFixed : ((fractionDigits?: number) => string) | undefined + +}); + === /main.ts === import { checkedArray, checkedObject } from "./checked"; >checkedArray : ([required, optional,]?: [any?, (boolean | undefined)?]) => void diff --git a/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts b/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts index 7ba77677d6433..4ede550cb4a36 100644 --- a/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts +++ b/tsc/testdata/tests/cases/compiler/objectBindingPatternDefaultMissingElements.ts @@ -2,6 +2,7 @@ // @checkJs: false // @noEmit: true // @strict: true +// @lib: es5,es2015.promise // @filename: /unchecked.js export const uncheckedObject = ({ @@ -26,6 +27,23 @@ export const checkedArray = ([ optional = false, ] = []) => {}; +/** @typedef {{ a?: number }} A */ +/** @typedef {{ f: (b: string, c?: A) => Promise }} B */ +/** @returns {B} */ +export function f15() { + return { + async f(b, { a } = {}) { + a?.toFixed(); + return [b]; + }, + }; +} + +/** @typedef {(b: number, c?: A) => Promise} F */ +export const f16 = /** @type {F} */ (async (b, { a } = {}) => { + a?.toFixed(); +}); + // @filename: /main.ts import { checkedArray, checkedObject } from "./checked"; import { uncheckedArray, uncheckedObject } from "./unchecked";