diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 128e6410cb432..a965651486b1d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11775,7 +11775,8 @@ namespace ts { const propTypes: Type[] = []; for (const prop of getPropertiesOfType(type)) { if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) { - propTypes.push(getTypeOfSymbol(prop)); + const propType = getTypeOfSymbol(prop); + propTypes.push(prop.flags & SymbolFlags.Optional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) : propType); } } if (kind === IndexKind.String) { diff --git a/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.js b/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.js new file mode 100644 index 0000000000000..386b08fbc0047 --- /dev/null +++ b/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.js @@ -0,0 +1,40 @@ +//// [inferenceOptionalPropertiesToIndexSignatures.ts] +declare function foo(obj: { [x: string]: T }): T; + +declare const x1: { a: string, b: number }; +declare const x2: { a: string, b: number | undefined }; +declare const x3: { a: string, b?: number }; +declare const x4: { a: string, b?: number | undefined }; + +let a1 = foo(x1); // string | number +let a2 = foo(x2); // string | number | undefined +let a3 = foo(x3); // string | number +let a4 = foo(x4); // string | number + +// Repro from #43045 + +const param2 = Math.random() < 0.5 ? 'value2' : null; + +const obj = { + param1: 'value1', + ...(param2 ? {param2} : {}) +}; + +const query = Object.entries(obj).map( + ([k, v]) => `${k}=${encodeURIComponent(v)}` +).join('&'); + + +//// [inferenceOptionalPropertiesToIndexSignatures.js] +"use strict"; +let a1 = foo(x1); // string | number +let a2 = foo(x2); // string | number | undefined +let a3 = foo(x3); // string | number +let a4 = foo(x4); // string | number +// Repro from #43045 +const param2 = Math.random() < 0.5 ? 'value2' : null; +const obj = { + param1: 'value1', + ...(param2 ? { param2 } : {}) +}; +const query = Object.entries(obj).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&'); diff --git a/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.symbols b/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.symbols new file mode 100644 index 0000000000000..9874e516a2b75 --- /dev/null +++ b/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.symbols @@ -0,0 +1,89 @@ +=== tests/cases/compiler/inferenceOptionalPropertiesToIndexSignatures.ts === +declare function foo(obj: { [x: string]: T }): T; +>foo : Symbol(foo, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 0)) +>T : Symbol(T, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 21)) +>obj : Symbol(obj, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 24)) +>x : Symbol(x, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 32)) +>T : Symbol(T, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 21)) +>T : Symbol(T, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 21)) + +declare const x1: { a: string, b: number }; +>x1 : Symbol(x1, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 2, 13)) +>a : Symbol(a, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 2, 19)) +>b : Symbol(b, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 2, 30)) + +declare const x2: { a: string, b: number | undefined }; +>x2 : Symbol(x2, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 3, 13)) +>a : Symbol(a, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 3, 19)) +>b : Symbol(b, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 3, 30)) + +declare const x3: { a: string, b?: number }; +>x3 : Symbol(x3, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 4, 13)) +>a : Symbol(a, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 4, 19)) +>b : Symbol(b, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 4, 30)) + +declare const x4: { a: string, b?: number | undefined }; +>x4 : Symbol(x4, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 5, 13)) +>a : Symbol(a, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 5, 19)) +>b : Symbol(b, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 5, 30)) + +let a1 = foo(x1); // string | number +>a1 : Symbol(a1, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 7, 3)) +>foo : Symbol(foo, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 0)) +>x1 : Symbol(x1, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 2, 13)) + +let a2 = foo(x2); // string | number | undefined +>a2 : Symbol(a2, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 8, 3)) +>foo : Symbol(foo, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 0)) +>x2 : Symbol(x2, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 3, 13)) + +let a3 = foo(x3); // string | number +>a3 : Symbol(a3, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 9, 3)) +>foo : Symbol(foo, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 0)) +>x3 : Symbol(x3, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 4, 13)) + +let a4 = foo(x4); // string | number +>a4 : Symbol(a4, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 10, 3)) +>foo : Symbol(foo, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 0, 0)) +>x4 : Symbol(x4, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 5, 13)) + +// Repro from #43045 + +const param2 = Math.random() < 0.5 ? 'value2' : null; +>param2 : Symbol(param2, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 14, 5)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + +const obj = { +>obj : Symbol(obj, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 16, 5)) + + param1: 'value1', +>param1 : Symbol(param1, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 16, 13)) + + ...(param2 ? {param2} : {}) +>param2 : Symbol(param2, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 14, 5)) +>param2 : Symbol(param2, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 18, 18)) + +}; + +const query = Object.entries(obj).map( +>query : Symbol(query, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 21, 5)) +>Object.entries(obj).map( ([k, v]) => `${k}=${encodeURIComponent(v)}`).join : Symbol(Array.join, Decl(lib.es5.d.ts, --, --)) +>Object.entries(obj).map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>obj : Symbol(obj, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 16, 5)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) + + ([k, v]) => `${k}=${encodeURIComponent(v)}` +>k : Symbol(k, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 22, 6)) +>v : Symbol(v, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 22, 8)) +>k : Symbol(k, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 22, 6)) +>encodeURIComponent : Symbol(encodeURIComponent, Decl(lib.es5.d.ts, --, --)) +>v : Symbol(v, Decl(inferenceOptionalPropertiesToIndexSignatures.ts, 22, 8)) + +).join('&'); +>join : Symbol(Array.join, Decl(lib.es5.d.ts, --, --)) + diff --git a/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.types b/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.types new file mode 100644 index 0000000000000..b707b156b2ea2 --- /dev/null +++ b/tests/baselines/reference/inferenceOptionalPropertiesToIndexSignatures.types @@ -0,0 +1,109 @@ +=== tests/cases/compiler/inferenceOptionalPropertiesToIndexSignatures.ts === +declare function foo(obj: { [x: string]: T }): T; +>foo : (obj: { [x: string]: T; }) => T +>obj : { [x: string]: T; } +>x : string + +declare const x1: { a: string, b: number }; +>x1 : { a: string; b: number; } +>a : string +>b : number + +declare const x2: { a: string, b: number | undefined }; +>x2 : { a: string; b: number | undefined; } +>a : string +>b : number | undefined + +declare const x3: { a: string, b?: number }; +>x3 : { a: string; b?: number | undefined; } +>a : string +>b : number | undefined + +declare const x4: { a: string, b?: number | undefined }; +>x4 : { a: string; b?: number | undefined; } +>a : string +>b : number | undefined + +let a1 = foo(x1); // string | number +>a1 : string | number +>foo(x1) : string | number +>foo : (obj: { [x: string]: T; }) => T +>x1 : { a: string; b: number; } + +let a2 = foo(x2); // string | number | undefined +>a2 : string | number | undefined +>foo(x2) : string | number | undefined +>foo : (obj: { [x: string]: T; }) => T +>x2 : { a: string; b: number | undefined; } + +let a3 = foo(x3); // string | number +>a3 : string | number +>foo(x3) : string | number +>foo : (obj: { [x: string]: T; }) => T +>x3 : { a: string; b?: number | undefined; } + +let a4 = foo(x4); // string | number +>a4 : string | number +>foo(x4) : string | number +>foo : (obj: { [x: string]: T; }) => T +>x4 : { a: string; b?: number | undefined; } + +// Repro from #43045 + +const param2 = Math.random() < 0.5 ? 'value2' : null; +>param2 : "value2" | null +>Math.random() < 0.5 ? 'value2' : null : "value2" | null +>Math.random() < 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 +>'value2' : "value2" +>null : null + +const obj = { +>obj : { param2?: string | undefined; param1: string; } +>{ param1: 'value1', ...(param2 ? {param2} : {})} : { param2?: string | undefined; param1: string; } + + param1: 'value1', +>param1 : string +>'value1' : "value1" + + ...(param2 ? {param2} : {}) +>(param2 ? {param2} : {}) : { param2: string; } | {} +>param2 ? {param2} : {} : { param2: string; } | {} +>param2 : "value2" | null +>{param2} : { param2: string; } +>param2 : string +>{} : {} + +}; + +const query = Object.entries(obj).map( +>query : string +>Object.entries(obj).map( ([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&') : string +>Object.entries(obj).map( ([k, v]) => `${k}=${encodeURIComponent(v)}`).join : (separator?: string | undefined) => string +>Object.entries(obj).map( ([k, v]) => `${k}=${encodeURIComponent(v)}`) : string[] +>Object.entries(obj).map : (callbackfn: (value: [string, string], index: number, array: [string, string][]) => U, thisArg?: any) => U[] +>Object.entries(obj) : [string, string][] +>Object.entries : { (o: { [s: string]: T; } | ArrayLike): [string, T][]; (o: {}): [string, any][]; } +>Object : ObjectConstructor +>entries : { (o: { [s: string]: T; } | ArrayLike): [string, T][]; (o: {}): [string, any][]; } +>obj : { param2?: string | undefined; param1: string; } +>map : (callbackfn: (value: [string, string], index: number, array: [string, string][]) => U, thisArg?: any) => U[] + + ([k, v]) => `${k}=${encodeURIComponent(v)}` +>([k, v]) => `${k}=${encodeURIComponent(v)}` : ([k, v]: [string, string]) => string +>k : string +>v : string +>`${k}=${encodeURIComponent(v)}` : string +>k : string +>encodeURIComponent(v) : string +>encodeURIComponent : (uriComponent: string | number | boolean) => string +>v : string + +).join('&'); +>join : (separator?: string | undefined) => string +>'&' : "&" + diff --git a/tests/cases/compiler/inferenceOptionalPropertiesToIndexSignatures.ts b/tests/cases/compiler/inferenceOptionalPropertiesToIndexSignatures.ts new file mode 100644 index 0000000000000..cd675e2facd5d --- /dev/null +++ b/tests/cases/compiler/inferenceOptionalPropertiesToIndexSignatures.ts @@ -0,0 +1,27 @@ +// @strict: true +// @target: esnext + +declare function foo(obj: { [x: string]: T }): T; + +declare const x1: { a: string, b: number }; +declare const x2: { a: string, b: number | undefined }; +declare const x3: { a: string, b?: number }; +declare const x4: { a: string, b?: number | undefined }; + +let a1 = foo(x1); // string | number +let a2 = foo(x2); // string | number | undefined +let a3 = foo(x3); // string | number +let a4 = foo(x4); // string | number + +// Repro from #43045 + +const param2 = Math.random() < 0.5 ? 'value2' : null; + +const obj = { + param1: 'value1', + ...(param2 ? {param2} : {}) +}; + +const query = Object.entries(obj).map( + ([k, v]) => `${k}=${encodeURIComponent(v)}` +).join('&');