diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c8869d9ae2fe3..aa4ea4eaa79f2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14670,19 +14670,18 @@ namespace ts { } function tryMergeUnionOfObjectTypeAndEmptyObject(type: UnionType, readonly: boolean): Type | undefined { - if (type.types.length === 2) { - const firstType = type.types[0]; - const secondType = type.types[1]; - if (every(type.types, isEmptyObjectTypeOrSpreadsIntoEmptyObject)) { - return isEmptyObjectType(firstType) ? firstType : isEmptyObjectType(secondType) ? secondType : emptyObjectType; - } - if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(firstType)) { - return getAnonymousPartialType(secondType); - } - if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(secondType)) { - return getAnonymousPartialType(firstType); - } + if (every(type.types, isEmptyObjectTypeOrSpreadsIntoEmptyObject)) { + return find(type.types, isEmptyObjectType) || emptyObjectType; + } + const firstType = find(type.types, t => !isEmptyObjectTypeOrSpreadsIntoEmptyObject(t)); + if (!firstType) { + return undefined; + } + const secondType = firstType && find(type.types, t => t !== firstType && !isEmptyObjectTypeOrSpreadsIntoEmptyObject(t)); + if (secondType) { + return undefined; } + return getAnonymousPartialType(firstType); function getAnonymousPartialType(type: Type) { // gets the type as if it had been spread, but where everything in the spread is made optional @@ -14695,7 +14694,7 @@ namespace ts { const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor); const flags = SymbolFlags.Property | SymbolFlags.Optional; const result = createSymbol(flags, prop.escapedName, getIsLateCheckFlag(prop) | (readonly ? CheckFlags.Readonly : 0)); - result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); + result.type = isSetonlyAccessor ? undefinedType : getUnionType([getTypeOfSymbol(prop), undefinedType]); result.declarations = prop.declarations; result.nameType = getSymbolLinks(prop).nameType; result.syntheticOrigin = prop; diff --git a/tests/baselines/reference/objectSpread.types b/tests/baselines/reference/objectSpread.types index 45c4c853faa39..7fd7675d1d73d 100644 --- a/tests/baselines/reference/objectSpread.types +++ b/tests/baselines/reference/objectSpread.types @@ -229,8 +229,8 @@ function conditionalSpreadBoolean(b: boolean) : { x: number, y: number } { >14 : 14 } let o2 = { ...b && { x: 21 }} ->o2 : { x?: number; } ->{ ...b && { x: 21 }} : { x?: number; } +>o2 : { x?: number | undefined; } +>{ ...b && { x: 21 }} : { x?: number | undefined; } >b && { x: 21 } : false | { x: number; } >b : boolean >{ x: 21 } : { x: number; } @@ -270,8 +270,8 @@ function conditionalSpreadNumber(nt: number): { x: number, y: number } { >nt : number } let o2 = { ...nt && { x: nt }} ->o2 : { x?: number; } ->{ ...nt && { x: nt }} : { x?: number; } +>o2 : { x?: number | undefined; } +>{ ...nt && { x: nt }} : { x?: number | undefined; } >nt && { x: nt } : 0 | { x: number; } >nt : number >{ x: nt } : { x: number; } @@ -311,8 +311,8 @@ function conditionalSpreadString(st: string): { x: string, y: number } { >st : string } let o2 = { ...st && { x: st }} ->o2 : { x?: string; } ->{ ...st && { x: st }} : { x?: string; } +>o2 : { x?: string | undefined; } +>{ ...st && { x: st }} : { x?: string | undefined; } >st && { x: st } : "" | { x: string; } >st : string >{ x: st } : { x: string; } diff --git a/tests/baselines/reference/objectSpreadRepeatedComplexity.types b/tests/baselines/reference/objectSpreadRepeatedComplexity.types index ad44309a2177d..376a7db385ea6 100644 --- a/tests/baselines/reference/objectSpreadRepeatedComplexity.types +++ b/tests/baselines/reference/objectSpreadRepeatedComplexity.types @@ -1,11 +1,11 @@ === tests/cases/conformance/types/spread/objectSpreadRepeatedComplexity.ts === function f(cnd: Record){ ->f : (cnd: Record) => { prop20a?: number; prop20b?: number; prop19a?: number; prop19b?: number; prop18a?: number; prop18b?: number; prop17a?: number; prop17b?: number; prop16a?: number; prop16b?: number; prop15a?: number; prop15b?: number; prop14a?: number; prop14b?: number; prop13a?: number; prop13b?: number; prop12a?: number; prop12b?: number; prop11a?: number; prop11b?: number; prop10a?: number; prop10b?: number; prop9a?: number; prop9b?: number; prop8a?: number; prop8b?: number; prop7a?: number; prop7b?: number; prop6a?: number; prop6b?: number; prop5a?: number; prop5b?: number; prop4a?: number; prop4b?: number; prop3a?: number; prop3b?: number; prop0?: number; } +>f : (cnd: Record) => { prop20a?: number | undefined; prop20b?: number | undefined; prop19a?: number | undefined; prop19b?: number | undefined; prop18a?: number | undefined; prop18b?: number | undefined; prop17a?: number | undefined; prop17b?: number | undefined; prop16a?: number | undefined; prop16b?: number | undefined; prop15a?: number | undefined; prop15b?: number | undefined; prop14a?: number | undefined; prop14b?: number | undefined; prop13a?: number | undefined; prop13b?: number | undefined; prop12a?: number | undefined; prop12b?: number | undefined; prop11a?: number | undefined; prop11b?: number | undefined; prop10a?: number | undefined; prop10b?: number | undefined; prop9a?: number | undefined; prop9b?: number | undefined; prop8a?: number | undefined; prop8b?: number | undefined; prop7a?: number | undefined; prop7b?: number | undefined; prop6a?: number | undefined; prop6b?: number | undefined; prop5a?: number | undefined; prop5b?: number | undefined; prop4a?: number | undefined; prop4b?: number | undefined; prop3a?: number | undefined; prop3b?: number | undefined; prop0?: number | undefined; } >cnd : Record // Type is a union of 2^(n-1) members, where n is the number of spread objects return { ->{ // Without this one, it collapses to {} ? ...(cnd[1] && cnd[2] && { prop0: 0, }), // With one prop each, it collapses to a single object (#34853?) ...(cnd[3] && { prop3a: 1, prop3b: 1, }), ...(cnd[4] && { prop4a: 1, prop4b: 1, }), ...(cnd[5] && { prop5a: 1, prop5b: 1, }), ...(cnd[6] && { prop6a: 1, prop6b: 1, }), ...(cnd[7] && { prop7a: 1, prop7b: 1, }), ...(cnd[8] && { prop8a: 1, prop8b: 1, }), ...(cnd[9] && { prop9a: 1, prop9b: 1, }), ...(cnd[10] && { prop10a: 1, prop10b: 1, }), ...(cnd[11] && { prop11a: 1, prop11b: 1, }), ...(cnd[12] && { prop12a: 1, prop12b: 1, }), ...(cnd[13] && { prop13a: 1, prop13b: 1, }), ...(cnd[14] && { prop14a: 1, prop14b: 1, }), ...(cnd[15] && { prop15a: 1, prop15b: 1, }), ...(cnd[16] && { prop16a: 1, prop16b: 1, }), ...(cnd[17] && { prop17a: 1, prop17b: 1, }), ...(cnd[18] && { prop18a: 1, prop18b: 1, }), ...(cnd[19] && { prop19a: 1, prop19b: 1, }), ...(cnd[20] && { prop20a: 1, prop20b: 1, }), } : { prop20a?: number; prop20b?: number; prop19a?: number; prop19b?: number; prop18a?: number; prop18b?: number; prop17a?: number; prop17b?: number; prop16a?: number; prop16b?: number; prop15a?: number; prop15b?: number; prop14a?: number; prop14b?: number; prop13a?: number; prop13b?: number; prop12a?: number; prop12b?: number; prop11a?: number; prop11b?: number; prop10a?: number; prop10b?: number; prop9a?: number; prop9b?: number; prop8a?: number; prop8b?: number; prop7a?: number; prop7b?: number; prop6a?: number; prop6b?: number; prop5a?: number; prop5b?: number; prop4a?: number; prop4b?: number; prop3a?: number; prop3b?: number; prop0?: number; } +>{ // Without this one, it collapses to {} ? ...(cnd[1] && cnd[2] && { prop0: 0, }), // With one prop each, it collapses to a single object (#34853?) ...(cnd[3] && { prop3a: 1, prop3b: 1, }), ...(cnd[4] && { prop4a: 1, prop4b: 1, }), ...(cnd[5] && { prop5a: 1, prop5b: 1, }), ...(cnd[6] && { prop6a: 1, prop6b: 1, }), ...(cnd[7] && { prop7a: 1, prop7b: 1, }), ...(cnd[8] && { prop8a: 1, prop8b: 1, }), ...(cnd[9] && { prop9a: 1, prop9b: 1, }), ...(cnd[10] && { prop10a: 1, prop10b: 1, }), ...(cnd[11] && { prop11a: 1, prop11b: 1, }), ...(cnd[12] && { prop12a: 1, prop12b: 1, }), ...(cnd[13] && { prop13a: 1, prop13b: 1, }), ...(cnd[14] && { prop14a: 1, prop14b: 1, }), ...(cnd[15] && { prop15a: 1, prop15b: 1, }), ...(cnd[16] && { prop16a: 1, prop16b: 1, }), ...(cnd[17] && { prop17a: 1, prop17b: 1, }), ...(cnd[18] && { prop18a: 1, prop18b: 1, }), ...(cnd[19] && { prop19a: 1, prop19b: 1, }), ...(cnd[20] && { prop20a: 1, prop20b: 1, }), } : { prop20a?: number | undefined; prop20b?: number | undefined; prop19a?: number | undefined; prop19b?: number | undefined; prop18a?: number | undefined; prop18b?: number | undefined; prop17a?: number | undefined; prop17b?: number | undefined; prop16a?: number | undefined; prop16b?: number | undefined; prop15a?: number | undefined; prop15b?: number | undefined; prop14a?: number | undefined; prop14b?: number | undefined; prop13a?: number | undefined; prop13b?: number | undefined; prop12a?: number | undefined; prop12b?: number | undefined; prop11a?: number | undefined; prop11b?: number | undefined; prop10a?: number | undefined; prop10b?: number | undefined; prop9a?: number | undefined; prop9b?: number | undefined; prop8a?: number | undefined; prop8b?: number | undefined; prop7a?: number | undefined; prop7b?: number | undefined; prop6a?: number | undefined; prop6b?: number | undefined; prop5a?: number | undefined; prop5b?: number | undefined; prop4a?: number | undefined; prop4b?: number | undefined; prop3a?: number | undefined; prop3b?: number | undefined; prop0?: number | undefined; } // Without this one, it collapses to {} ? ...(cnd[1] && diff --git a/tests/baselines/reference/objectSpreadRepeatedNullCheckPerf.types b/tests/baselines/reference/objectSpreadRepeatedNullCheckPerf.types index d2f1e79037175..12f4789c3e390 100644 --- a/tests/baselines/reference/objectSpreadRepeatedNullCheckPerf.types +++ b/tests/baselines/reference/objectSpreadRepeatedNullCheckPerf.types @@ -84,7 +84,7 @@ function parseWithSpread(config: Record): Props { >config : Record return { ->{ ...config.a !== undefined && { a: config.a.toString() }, ...config.b !== undefined && { b: config.b.toString() }, ...config.c !== undefined && { c: config.c.toString() }, ...config.d !== undefined && { d: config.d.toString() }, ...config.e !== undefined && { e: config.e.toString() }, ...config.f !== undefined && { f: config.f.toString() }, ...config.g !== undefined && { g: config.g.toString() }, ...config.h !== undefined && { h: config.h.toString() }, ...config.i !== undefined && { i: config.i.toString() }, ...config.j !== undefined && { j: config.j.toString() }, ...config.k !== undefined && { k: config.k.toString() }, ...config.l !== undefined && { l: config.l.toString() }, ...config.m !== undefined && { m: config.m.toString() }, ...config.n !== undefined && { n: config.n.toString() }, ...config.o !== undefined && { o: config.o.toString() }, ...config.p !== undefined && { p: config.p.toString() }, ...config.q !== undefined && { q: config.q.toString() }, ...config.r !== undefined && { r: config.r.toString() }, ...config.s !== undefined && { s: config.s.toString() }, ...config.t !== undefined && { t: config.t.toString() }, ...config.u !== undefined && { u: config.u.toString() }, ...config.v !== undefined && { v: config.v.toString() }, ...config.w !== undefined && { w: config.w.toString() }, ...config.x !== undefined && { x: config.x.toString() }, ...config.y !== undefined && { y: config.y.toString() }, ...config.z !== undefined && { z: config.z.toString() } } : { z?: string; y?: string; x?: string; w?: string; v?: string; u?: string; t?: string; s?: string; r?: string; q?: string; p?: string; o?: string; n?: string; m?: string; l?: string; k?: string; j?: string; i?: string; h?: string; g?: string; f?: string; e?: string; d?: string; c?: string; b?: string; a?: string; } +>{ ...config.a !== undefined && { a: config.a.toString() }, ...config.b !== undefined && { b: config.b.toString() }, ...config.c !== undefined && { c: config.c.toString() }, ...config.d !== undefined && { d: config.d.toString() }, ...config.e !== undefined && { e: config.e.toString() }, ...config.f !== undefined && { f: config.f.toString() }, ...config.g !== undefined && { g: config.g.toString() }, ...config.h !== undefined && { h: config.h.toString() }, ...config.i !== undefined && { i: config.i.toString() }, ...config.j !== undefined && { j: config.j.toString() }, ...config.k !== undefined && { k: config.k.toString() }, ...config.l !== undefined && { l: config.l.toString() }, ...config.m !== undefined && { m: config.m.toString() }, ...config.n !== undefined && { n: config.n.toString() }, ...config.o !== undefined && { o: config.o.toString() }, ...config.p !== undefined && { p: config.p.toString() }, ...config.q !== undefined && { q: config.q.toString() }, ...config.r !== undefined && { r: config.r.toString() }, ...config.s !== undefined && { s: config.s.toString() }, ...config.t !== undefined && { t: config.t.toString() }, ...config.u !== undefined && { u: config.u.toString() }, ...config.v !== undefined && { v: config.v.toString() }, ...config.w !== undefined && { w: config.w.toString() }, ...config.x !== undefined && { x: config.x.toString() }, ...config.y !== undefined && { y: config.y.toString() }, ...config.z !== undefined && { z: config.z.toString() } } : { z?: string | undefined; y?: string | undefined; x?: string | undefined; w?: string | undefined; v?: string | undefined; u?: string | undefined; t?: string | undefined; s?: string | undefined; r?: string | undefined; q?: string | undefined; p?: string | undefined; o?: string | undefined; n?: string | undefined; m?: string | undefined; l?: string | undefined; k?: string | undefined; j?: string | undefined; i?: string | undefined; h?: string | undefined; g?: string | undefined; f?: string | undefined; e?: string | undefined; d?: string | undefined; c?: string | undefined; b?: string | undefined; a?: string | undefined; } ...config.a !== undefined && { a: config.a.toString() }, >config.a !== undefined && { a: config.a.toString() } : false | { a: string; } diff --git a/tests/baselines/reference/spreadIdenticalTypesRemoved.js b/tests/baselines/reference/spreadIdenticalTypesRemoved.js new file mode 100644 index 0000000000000..e920e8e01e4a0 --- /dev/null +++ b/tests/baselines/reference/spreadIdenticalTypesRemoved.js @@ -0,0 +1,48 @@ +//// [spreadIdenticalTypesRemoved.ts] +interface Animal { + name: string; + kind: string; + age: number; + location: string; + owner: object; +} + +function clonePet(pet: Animal, fullCopy?: boolean) { + return { + name: pet.name, + kind: pet.kind, + ...(fullCopy && pet), + } +} + +interface Animal2 { + name: string; + owner?: string; +} +function billOwner(pet: Animal2) { + return { + ...(pet.owner && pet), + paid: false + } +} + + +//// [spreadIdenticalTypesRemoved.js] +"use strict"; +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); +}; +function clonePet(pet, fullCopy) { + return __assign({ name: pet.name, kind: pet.kind }, (fullCopy && pet)); +} +function billOwner(pet) { + return __assign(__assign({}, (pet.owner && pet)), { paid: false }); +} diff --git a/tests/baselines/reference/spreadIdenticalTypesRemoved.symbols b/tests/baselines/reference/spreadIdenticalTypesRemoved.symbols new file mode 100644 index 0000000000000..75c82f95c18cd --- /dev/null +++ b/tests/baselines/reference/spreadIdenticalTypesRemoved.symbols @@ -0,0 +1,71 @@ +=== tests/cases/compiler/spreadIdenticalTypesRemoved.ts === +interface Animal { +>Animal : Symbol(Animal, Decl(spreadIdenticalTypesRemoved.ts, 0, 0)) + + name: string; +>name : Symbol(Animal.name, Decl(spreadIdenticalTypesRemoved.ts, 0, 18)) + + kind: string; +>kind : Symbol(Animal.kind, Decl(spreadIdenticalTypesRemoved.ts, 1, 17)) + + age: number; +>age : Symbol(Animal.age, Decl(spreadIdenticalTypesRemoved.ts, 2, 17)) + + location: string; +>location : Symbol(Animal.location, Decl(spreadIdenticalTypesRemoved.ts, 3, 16)) + + owner: object; +>owner : Symbol(Animal.owner, Decl(spreadIdenticalTypesRemoved.ts, 4, 21)) +} + +function clonePet(pet: Animal, fullCopy?: boolean) { +>clonePet : Symbol(clonePet, Decl(spreadIdenticalTypesRemoved.ts, 6, 1)) +>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 8, 18)) +>Animal : Symbol(Animal, Decl(spreadIdenticalTypesRemoved.ts, 0, 0)) +>fullCopy : Symbol(fullCopy, Decl(spreadIdenticalTypesRemoved.ts, 8, 30)) + + return { + name: pet.name, +>name : Symbol(name, Decl(spreadIdenticalTypesRemoved.ts, 9, 12)) +>pet.name : Symbol(Animal.name, Decl(spreadIdenticalTypesRemoved.ts, 0, 18)) +>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 8, 18)) +>name : Symbol(Animal.name, Decl(spreadIdenticalTypesRemoved.ts, 0, 18)) + + kind: pet.kind, +>kind : Symbol(kind, Decl(spreadIdenticalTypesRemoved.ts, 10, 23)) +>pet.kind : Symbol(Animal.kind, Decl(spreadIdenticalTypesRemoved.ts, 1, 17)) +>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 8, 18)) +>kind : Symbol(Animal.kind, Decl(spreadIdenticalTypesRemoved.ts, 1, 17)) + + ...(fullCopy && pet), +>fullCopy : Symbol(fullCopy, Decl(spreadIdenticalTypesRemoved.ts, 8, 30)) +>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 8, 18)) + } +} + +interface Animal2 { +>Animal2 : Symbol(Animal2, Decl(spreadIdenticalTypesRemoved.ts, 14, 1)) + + name: string; +>name : Symbol(Animal2.name, Decl(spreadIdenticalTypesRemoved.ts, 16, 19)) + + owner?: string; +>owner : Symbol(Animal2.owner, Decl(spreadIdenticalTypesRemoved.ts, 17, 17)) +} +function billOwner(pet: Animal2) { +>billOwner : Symbol(billOwner, Decl(spreadIdenticalTypesRemoved.ts, 19, 1)) +>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 20, 19)) +>Animal2 : Symbol(Animal2, Decl(spreadIdenticalTypesRemoved.ts, 14, 1)) + + return { + ...(pet.owner && pet), +>pet.owner : Symbol(Animal2.owner, Decl(spreadIdenticalTypesRemoved.ts, 17, 17)) +>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 20, 19)) +>owner : Symbol(Animal2.owner, Decl(spreadIdenticalTypesRemoved.ts, 17, 17)) +>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 20, 19)) + + paid: false +>paid : Symbol(paid, Decl(spreadIdenticalTypesRemoved.ts, 22, 30)) + } +} + diff --git a/tests/baselines/reference/spreadIdenticalTypesRemoved.types b/tests/baselines/reference/spreadIdenticalTypesRemoved.types new file mode 100644 index 0000000000000..6695770ccc3ed --- /dev/null +++ b/tests/baselines/reference/spreadIdenticalTypesRemoved.types @@ -0,0 +1,74 @@ +=== tests/cases/compiler/spreadIdenticalTypesRemoved.ts === +interface Animal { + name: string; +>name : string + + kind: string; +>kind : string + + age: number; +>age : number + + location: string; +>location : string + + owner: object; +>owner : object +} + +function clonePet(pet: Animal, fullCopy?: boolean) { +>clonePet : (pet: Animal, fullCopy?: boolean | undefined) => { name: string; kind: string; age?: number | undefined; location?: string | undefined; owner?: object | undefined; } +>pet : Animal +>fullCopy : boolean | undefined + + return { +>{ name: pet.name, kind: pet.kind, ...(fullCopy && pet), } : { name: string; kind: string; age?: number | undefined; location?: string | undefined; owner?: object | undefined; } + + name: pet.name, +>name : string +>pet.name : string +>pet : Animal +>name : string + + kind: pet.kind, +>kind : string +>pet.kind : string +>pet : Animal +>kind : string + + ...(fullCopy && pet), +>(fullCopy && pet) : false | Animal | undefined +>fullCopy && pet : false | Animal | undefined +>fullCopy : boolean | undefined +>pet : Animal + } +} + +interface Animal2 { + name: string; +>name : string + + owner?: string; +>owner : string | undefined +} +function billOwner(pet: Animal2) { +>billOwner : (pet: Animal2) => { paid: boolean; name?: string | undefined; owner?: string | undefined; } +>pet : Animal2 + + return { +>{ ...(pet.owner && pet), paid: false } : { paid: boolean; name?: string | undefined; owner?: string | undefined; } + + ...(pet.owner && pet), +>(pet.owner && pet) : "" | Animal2 | undefined +>pet.owner && pet : "" | Animal2 | undefined +>pet.owner : string | undefined +>pet : Animal2 +>owner : string | undefined +>pet : Animal2 + + paid: false +>paid : boolean +>false : false + } +} + diff --git a/tests/baselines/reference/spreadOfObjectLiteralAssignableToIndexSignature.types b/tests/baselines/reference/spreadOfObjectLiteralAssignableToIndexSignature.types index 53462c68c03ea..8b4fd5422a738 100644 --- a/tests/baselines/reference/spreadOfObjectLiteralAssignableToIndexSignature.types +++ b/tests/baselines/reference/spreadOfObjectLiteralAssignableToIndexSignature.types @@ -9,11 +9,11 @@ const recordOfRecords: RecordOfRecords = {} >{} : {} recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK ->recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} : { foo?: Record; } +>recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} : { foo?: Record | undefined; } >recordOfRecords.propA : RecordOfRecords >recordOfRecords : RecordOfRecords >propA : RecordOfRecords ->{...(foo !== undefined ? {foo} : {})} : { foo?: Record; } +>{...(foo !== undefined ? {foo} : {})} : { foo?: Record | undefined; } >(foo !== undefined ? {foo} : {}) : { foo: Record; } | {} >foo !== undefined ? {foo} : {} : { foo: Record; } | {} >foo !== undefined : boolean @@ -36,11 +36,11 @@ recordOfRecords.propB = {...(foo && {foo})} // OK >foo : Record recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK ->recordOfRecords.propC = {...(foo !== undefined && {foo})} : { foo?: Record; } +>recordOfRecords.propC = {...(foo !== undefined && {foo})} : { foo?: Record | undefined; } >recordOfRecords.propC : RecordOfRecords >recordOfRecords : RecordOfRecords >propC : RecordOfRecords ->{...(foo !== undefined && {foo})} : { foo?: Record; } +>{...(foo !== undefined && {foo})} : { foo?: Record | undefined; } >(foo !== undefined && {foo}) : false | { foo: Record; } >foo !== undefined && {foo} : false | { foo: Record; } >foo !== undefined : boolean @@ -55,11 +55,11 @@ const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {} >{} : {} recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK ->recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} : { foo?: Record; } +>recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} : { foo?: Record | undefined; } >recordsOfRecordsOrEmpty.propA : {} | RecordOfRecordsOrEmpty >recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty >propA : {} | RecordOfRecordsOrEmpty ->{...(foo !== undefined ? {foo} : {})} : { foo?: Record; } +>{...(foo !== undefined ? {foo} : {})} : { foo?: Record | undefined; } >(foo !== undefined ? {foo} : {}) : { foo: Record; } | {} >foo !== undefined ? {foo} : {} : { foo: Record; } | {} >foo !== undefined : boolean @@ -82,11 +82,11 @@ recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK >foo : Record recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK ->recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} : { foo?: Record; } +>recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} : { foo?: Record | undefined; } >recordsOfRecordsOrEmpty.propC : {} | RecordOfRecordsOrEmpty >recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty >propC : {} | RecordOfRecordsOrEmpty ->{...(foo !== undefined && {foo})} : { foo?: Record; } +>{...(foo !== undefined && {foo})} : { foo?: Record | undefined; } >(foo !== undefined && {foo}) : false | { foo: Record; } >foo !== undefined && {foo} : false | { foo: Record; } >foo !== undefined : boolean diff --git a/tests/baselines/reference/spreadOverwritesPropertyStrict.types b/tests/baselines/reference/spreadOverwritesPropertyStrict.types index 66499cd0f6607..62a109a410f9a 100644 --- a/tests/baselines/reference/spreadOverwritesPropertyStrict.types +++ b/tests/baselines/reference/spreadOverwritesPropertyStrict.types @@ -78,14 +78,14 @@ function h(obj: { x: number } | { x: string }) { >obj : { x: number; } | { x: string; } } function i(b: boolean, t: { command: string, ok: string }) { ->i : (b: boolean, t: { command: string; ok: string;}) => { command: string; ok?: string; } +>i : (b: boolean, t: { command: string; ok: string;}) => { command: string; ok?: string | undefined; } >b : boolean >t : { command: string; ok: string; } >command : string >ok : string return { command: "hi", ...(b ? t : {}) } // ok ->{ command: "hi", ...(b ? t : {}) } : { command: string; ok?: string; } +>{ command: "hi", ...(b ? t : {}) } : { command: string; ok?: string | undefined; } >command : string >"hi" : "hi" >(b ? t : {}) : { command: string; ok: string; } | {} diff --git a/tests/baselines/reference/spreadUnion2.errors.txt b/tests/baselines/reference/spreadUnion2.errors.txt index 104998996a766..032fa52ba5e98 100644 --- a/tests/baselines/reference/spreadUnion2.errors.txt +++ b/tests/baselines/reference/spreadUnion2.errors.txt @@ -1,9 +1,9 @@ -tests/cases/conformance/types/spread/spreadUnion2.ts(5,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o1' must be of type '{} | { a: number; }', but here has type '{ a?: number; }'. -tests/cases/conformance/types/spread/spreadUnion2.ts(8,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o2' must be of type '{} | { b: number; }', but here has type '{ b?: number; }'. -tests/cases/conformance/types/spread/spreadUnion2.ts(11,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o3' must be of type '{} | { a: number; } | { b: number; } | { a: number; b: number; }', but here has type '{ b?: number; a?: number; }'. -tests/cases/conformance/types/spread/spreadUnion2.ts(12,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o3' must be of type '{} | { a: number; } | { b: number; } | { a: number; b: number; }', but here has type '{ a?: number; b?: number; }'. -tests/cases/conformance/types/spread/spreadUnion2.ts(15,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o4' must be of type '{} | { a: number; }', but here has type '{ a?: number; }'. -tests/cases/conformance/types/spread/spreadUnion2.ts(18,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o5' must be of type '{} | { b: number; }', but here has type '{ b?: number; }'. +tests/cases/conformance/types/spread/spreadUnion2.ts(5,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o1' must be of type '{} | { a: number; }', but here has type '{ a?: number | undefined; }'. +tests/cases/conformance/types/spread/spreadUnion2.ts(8,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o2' must be of type '{} | { b: number; }', but here has type '{ b?: number | undefined; }'. +tests/cases/conformance/types/spread/spreadUnion2.ts(11,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o3' must be of type '{} | { a: number; } | { b: number; } | { a: number; b: number; }', but here has type '{ b?: number | undefined; a?: number | undefined; }'. +tests/cases/conformance/types/spread/spreadUnion2.ts(12,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o3' must be of type '{} | { a: number; } | { b: number; } | { a: number; b: number; }', but here has type '{ a?: number | undefined; b?: number | undefined; }'. +tests/cases/conformance/types/spread/spreadUnion2.ts(15,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o4' must be of type '{} | { a: number; }', but here has type '{ a?: number | undefined; }'. +tests/cases/conformance/types/spread/spreadUnion2.ts(18,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'o5' must be of type '{} | { b: number; }', but here has type '{ b?: number | undefined; }'. ==== tests/cases/conformance/types/spread/spreadUnion2.ts (6 errors) ==== @@ -13,35 +13,35 @@ tests/cases/conformance/types/spread/spreadUnion2.ts(18,5): error TS2403: Subseq var o1: {} | { a: number }; var o1 = { ...undefinedUnion }; ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o1' must be of type '{} | { a: number; }', but here has type '{ a?: number; }'. +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o1' must be of type '{} | { a: number; }', but here has type '{ a?: number | undefined; }'. !!! related TS6203 tests/cases/conformance/types/spread/spreadUnion2.ts:4:5: 'o1' was also declared here. var o2: {} | { b: number }; var o2 = { ...nullUnion }; ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o2' must be of type '{} | { b: number; }', but here has type '{ b?: number; }'. +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o2' must be of type '{} | { b: number; }', but here has type '{ b?: number | undefined; }'. !!! related TS6203 tests/cases/conformance/types/spread/spreadUnion2.ts:7:5: 'o2' was also declared here. var o3: {} | { a: number } | { b: number } | { a: number, b: number }; var o3 = { ...undefinedUnion, ...nullUnion }; ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o3' must be of type '{} | { a: number; } | { b: number; } | { a: number; b: number; }', but here has type '{ b?: number; a?: number; }'. +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o3' must be of type '{} | { a: number; } | { b: number; } | { a: number; b: number; }', but here has type '{ b?: number | undefined; a?: number | undefined; }'. !!! related TS6203 tests/cases/conformance/types/spread/spreadUnion2.ts:10:5: 'o3' was also declared here. var o3 = { ...nullUnion, ...undefinedUnion }; ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o3' must be of type '{} | { a: number; } | { b: number; } | { a: number; b: number; }', but here has type '{ a?: number; b?: number; }'. +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o3' must be of type '{} | { a: number; } | { b: number; } | { a: number; b: number; }', but here has type '{ a?: number | undefined; b?: number | undefined; }'. !!! related TS6203 tests/cases/conformance/types/spread/spreadUnion2.ts:10:5: 'o3' was also declared here. var o4: {} | { a: number }; var o4 = { ...undefinedUnion, ...undefinedUnion }; ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o4' must be of type '{} | { a: number; }', but here has type '{ a?: number; }'. +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o4' must be of type '{} | { a: number; }', but here has type '{ a?: number | undefined; }'. !!! related TS6203 tests/cases/conformance/types/spread/spreadUnion2.ts:14:5: 'o4' was also declared here. var o5: {} | { b: number }; var o5 = { ...nullUnion, ...nullUnion }; ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o5' must be of type '{} | { b: number; }', but here has type '{ b?: number; }'. +!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'o5' must be of type '{} | { b: number; }', but here has type '{ b?: number | undefined; }'. !!! related TS6203 tests/cases/conformance/types/spread/spreadUnion2.ts:17:5: 'o5' was also declared here. \ No newline at end of file diff --git a/tests/baselines/reference/spreadUnion2.types b/tests/baselines/reference/spreadUnion2.types index bbb1a50eec6e3..93a55a08c3b16 100644 --- a/tests/baselines/reference/spreadUnion2.types +++ b/tests/baselines/reference/spreadUnion2.types @@ -14,7 +14,7 @@ var o1: {} | { a: number }; var o1 = { ...undefinedUnion }; >o1 : {} | { a: number; } ->{ ...undefinedUnion } : { a?: number; } +>{ ...undefinedUnion } : { a?: number | undefined; } >undefinedUnion : { a: number; } | undefined var o2: {} | { b: number }; @@ -23,7 +23,7 @@ var o2: {} | { b: number }; var o2 = { ...nullUnion }; >o2 : {} | { b: number; } ->{ ...nullUnion } : { b?: number; } +>{ ...nullUnion } : { b?: number | undefined; } >nullUnion : { b: number; } | null var o3: {} | { a: number } | { b: number } | { a: number, b: number }; @@ -35,13 +35,13 @@ var o3: {} | { a: number } | { b: number } | { a: number, b: number }; var o3 = { ...undefinedUnion, ...nullUnion }; >o3 : {} | { a: number; } | { b: number; } | { a: number; b: number; } ->{ ...undefinedUnion, ...nullUnion } : { b?: number; a?: number; } +>{ ...undefinedUnion, ...nullUnion } : { b?: number | undefined; a?: number | undefined; } >undefinedUnion : { a: number; } | undefined >nullUnion : { b: number; } | null var o3 = { ...nullUnion, ...undefinedUnion }; >o3 : {} | { a: number; } | { b: number; } | { a: number; b: number; } ->{ ...nullUnion, ...undefinedUnion } : { a?: number; b?: number; } +>{ ...nullUnion, ...undefinedUnion } : { a?: number | undefined; b?: number | undefined; } >nullUnion : { b: number; } | null >undefinedUnion : { a: number; } | undefined @@ -51,7 +51,7 @@ var o4: {} | { a: number }; var o4 = { ...undefinedUnion, ...undefinedUnion }; >o4 : {} | { a: number; } ->{ ...undefinedUnion, ...undefinedUnion } : { a?: number; } +>{ ...undefinedUnion, ...undefinedUnion } : { a?: number | undefined; } >undefinedUnion : { a: number; } | undefined >undefinedUnion : { a: number; } | undefined @@ -61,7 +61,7 @@ var o5: {} | { b: number }; var o5 = { ...nullUnion, ...nullUnion }; >o5 : {} | { b: number; } ->{ ...nullUnion, ...nullUnion } : { b?: number; } +>{ ...nullUnion, ...nullUnion } : { b?: number | undefined; } >nullUnion : { b: number; } | null >nullUnion : { b: number; } | null diff --git a/tests/baselines/reference/spreadUnion3.errors.txt b/tests/baselines/reference/spreadUnion3.errors.txt index 88aa12c708fb8..231c04d1d999a 100644 --- a/tests/baselines/reference/spreadUnion3.errors.txt +++ b/tests/baselines/reference/spreadUnion3.errors.txt @@ -1,5 +1,6 @@ tests/cases/conformance/types/spread/spreadUnion3.ts(2,14): error TS2322: Type 'number' is not assignable to type 'string'. -tests/cases/conformance/types/spread/spreadUnion3.ts(9,23): error TS2339: Property 'a' does not exist on type '{}'. +tests/cases/conformance/types/spread/spreadUnion3.ts(9,9): error TS2322: Type 'number | undefined' is not assignable to type 'number'. + Type 'undefined' is not assignable to type 'number'. tests/cases/conformance/types/spread/spreadUnion3.ts(17,11): error TS2698: Spread types may only be created from object types. tests/cases/conformance/types/spread/spreadUnion3.ts(18,11): error TS2698: Spread types may only be created from object types. @@ -17,8 +18,9 @@ tests/cases/conformance/types/spread/spreadUnion3.ts(18,11): error TS2698: Sprea function g(t?: { a: number } | null): void { let b = { ...t }; let c: number = b.a; // might not have 'a' - ~ -!!! error TS2339: Property 'a' does not exist on type '{}'. + ~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'number'. +!!! error TS2322: Type 'undefined' is not assignable to type 'number'. } g() g(undefined) diff --git a/tests/baselines/reference/spreadUnion3.symbols b/tests/baselines/reference/spreadUnion3.symbols index f345b650c4c6c..821499b9b5f70 100644 --- a/tests/baselines/reference/spreadUnion3.symbols +++ b/tests/baselines/reference/spreadUnion3.symbols @@ -25,7 +25,9 @@ function g(t?: { a: number } | null): void { let c: number = b.a; // might not have 'a' >c : Symbol(c, Decl(spreadUnion3.ts, 8, 7)) +>b.a : Symbol(a, Decl(spreadUnion3.ts, 6, 16)) >b : Symbol(b, Decl(spreadUnion3.ts, 7, 7)) +>a : Symbol(a, Decl(spreadUnion3.ts, 6, 16)) } g() >g : Symbol(g, Decl(spreadUnion3.ts, 3, 12)) diff --git a/tests/baselines/reference/spreadUnion3.types b/tests/baselines/reference/spreadUnion3.types index 33eec98dc4bc2..62a2fa2da733c 100644 --- a/tests/baselines/reference/spreadUnion3.types +++ b/tests/baselines/reference/spreadUnion3.types @@ -24,15 +24,15 @@ function g(t?: { a: number } | null): void { >null : null let b = { ...t }; ->b : {} ->{ ...t } : {} | {} | { a: number; } +>b : { a?: number | undefined; } +>{ ...t } : { a?: number | undefined; } >t : { a: number; } | null | undefined let c: number = b.a; // might not have 'a' >c : number ->b.a : any ->b : {} ->a : any +>b.a : number | undefined +>b : { a?: number | undefined; } +>a : number | undefined } g() >g() : void diff --git a/tests/cases/compiler/spreadIdenticalTypesRemoved.ts b/tests/cases/compiler/spreadIdenticalTypesRemoved.ts new file mode 100644 index 0000000000000..b63172d928367 --- /dev/null +++ b/tests/cases/compiler/spreadIdenticalTypesRemoved.ts @@ -0,0 +1,27 @@ +// @strict: true +interface Animal { + name: string; + kind: string; + age: number; + location: string; + owner: object; +} + +function clonePet(pet: Animal, fullCopy?: boolean) { + return { + name: pet.name, + kind: pet.kind, + ...(fullCopy && pet), + } +} + +interface Animal2 { + name: string; + owner?: string; +} +function billOwner(pet: Animal2) { + return { + ...(pet.owner && pet), + paid: false + } +}