Map stale empty object type in union into fresh empty object type after spread is complete (#34839)

* Map stale empty object type in union into fresh empty object type after spread is complete

* Accept minor baseline diff
This commit is contained in:
Wesley Wigham 2019-11-21 10:57:55 -08:00 committed by GitHub
parent 0d993ac592
commit a6d44aa52e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 214 additions and 4 deletions

View file

@ -22090,8 +22090,13 @@ namespace ts {
if (spread !== emptyObjectType) {
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, objectFlags, inConstContext);
propertiesArray = [];
propertiesTable = createSymbolTable();
hasComputedStringProperty = false;
hasComputedNumberProperty = false;
}
return spread;
// remap the raw emptyObjectType fed in at the top into a fresh empty object literal type, unique to this use site
return mapType(spread, t => t === emptyObjectType ? createObjectLiteralType() : t);
}
return createObjectLiteralType();

View file

@ -293,7 +293,7 @@ function conditionalSpreadBoolean(b: boolean) : { x: number, y: number } {
}
let o2 = { ...b && { x: 21 }}
>o2 : {}
>{ ...b && { x: 21 }} : {} | { x: number; }
>{ ...b && { x: 21 }} : { x: number; } | {}
>b && { x: 21 } : false | { x: number; }
>b : boolean
>{ x: 21 } : { x: number; }
@ -334,7 +334,7 @@ function conditionalSpreadNumber(nt: number): { x: number, y: number } {
}
let o2 = { ...nt && { x: nt }}
>o2 : {}
>{ ...nt && { x: nt }} : {} | { x: number; }
>{ ...nt && { x: nt }} : { x: number; } | {}
>nt && { x: nt } : 0 | { x: number; }
>nt : number
>{ x: nt } : { x: number; }
@ -375,7 +375,7 @@ function conditionalSpreadString(st: string): { x: string, y: number } {
}
let o2 = { ...st && { x: st }}
>o2 : {}
>{ ...st && { x: st }} : {} | { x: string; }
>{ ...st && { x: st }} : { x: string; } | {}
>st && { x: st } : "" | { x: string; }
>st : string
>{ x: st } : { x: string; }

View file

@ -0,0 +1,37 @@
//// [spreadOfObjectLiteralAssignableToIndexSignature.ts]
const foo: Record<never, never> = {} // OK
interface RecordOfRecords extends Record<keyof any, RecordOfRecords> {}
const recordOfRecords: RecordOfRecords = {}
recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK
recordOfRecords.propB = {...(foo && {foo})} // OK
recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK
interface RecordOfRecordsOrEmpty extends Record<keyof any, RecordOfRecordsOrEmpty | {}> {}
const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {}
recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK
recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK
recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK
//// [spreadOfObjectLiteralAssignableToIndexSignature.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);
};
var foo = {}; // OK
var recordOfRecords = {};
recordOfRecords.propA = __assign({}, (foo !== undefined ? { foo: foo } : {})); // OK
recordOfRecords.propB = __assign({}, (foo && { foo: foo })); // OK
recordOfRecords.propC = __assign({}, (foo !== undefined && { foo: foo })); // error'd in 3.7 beta, should be OK
var recordsOfRecordsOrEmpty = {};
recordsOfRecordsOrEmpty.propA = __assign({}, (foo !== undefined ? { foo: foo } : {})); // OK
recordsOfRecordsOrEmpty.propB = __assign({}, (foo && { foo: foo })); // OK
recordsOfRecordsOrEmpty.propC = __assign({}, (foo !== undefined && { foo: foo })); // OK

View file

@ -0,0 +1,57 @@
=== tests/cases/compiler/spreadOfObjectLiteralAssignableToIndexSignature.ts ===
const foo: Record<never, never> = {} // OK
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
interface RecordOfRecords extends Record<keyof any, RecordOfRecords> {}
>RecordOfRecords : Symbol(RecordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 36))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>RecordOfRecords : Symbol(RecordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 36))
const recordOfRecords: RecordOfRecords = {}
>recordOfRecords : Symbol(recordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 3, 5))
>RecordOfRecords : Symbol(RecordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 36))
recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK
>recordOfRecords : Symbol(recordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 3, 5))
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
>undefined : Symbol(undefined)
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 4, 50))
recordOfRecords.propB = {...(foo && {foo})} // OK
>recordOfRecords : Symbol(recordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 3, 5))
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 5, 37))
recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK
>recordOfRecords : Symbol(recordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 3, 5))
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
>undefined : Symbol(undefined)
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 6, 51))
interface RecordOfRecordsOrEmpty extends Record<keyof any, RecordOfRecordsOrEmpty | {}> {}
>RecordOfRecordsOrEmpty : Symbol(RecordOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 6, 57))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>RecordOfRecordsOrEmpty : Symbol(RecordOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 6, 57))
const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {}
>recordsOfRecordsOrEmpty : Symbol(recordsOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 9, 5))
>RecordOfRecordsOrEmpty : Symbol(RecordOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 6, 57))
recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK
>recordsOfRecordsOrEmpty : Symbol(recordsOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 9, 5))
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
>undefined : Symbol(undefined)
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 10, 58))
recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK
>recordsOfRecordsOrEmpty : Symbol(recordsOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 9, 5))
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 11, 45))
recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK
>recordsOfRecordsOrEmpty : Symbol(recordsOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 9, 5))
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
>undefined : Symbol(undefined)
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 12, 59))

View file

@ -0,0 +1,97 @@
=== tests/cases/compiler/spreadOfObjectLiteralAssignableToIndexSignature.ts ===
const foo: Record<never, never> = {} // OK
>foo : Record<never, never>
>{} : {}
interface RecordOfRecords extends Record<keyof any, RecordOfRecords> {}
const recordOfRecords: RecordOfRecords = {}
>recordOfRecords : RecordOfRecords
>{} : {}
recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK
>recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} : { foo: Record<never, never>; } | {}
>recordOfRecords.propA : RecordOfRecords
>recordOfRecords : RecordOfRecords
>propA : RecordOfRecords
>{...(foo !== undefined ? {foo} : {})} : { foo: Record<never, never>; } | {}
>(foo !== undefined ? {foo} : {}) : { foo: Record<never, never>; } | {}
>foo !== undefined ? {foo} : {} : { foo: Record<never, never>; } | {}
>foo !== undefined : boolean
>foo : Record<never, never>
>undefined : undefined
>{foo} : { foo: Record<never, never>; }
>foo : Record<never, never>
>{} : {}
recordOfRecords.propB = {...(foo && {foo})} // OK
>recordOfRecords.propB = {...(foo && {foo})} : { foo: Record<never, never>; }
>recordOfRecords.propB : RecordOfRecords
>recordOfRecords : RecordOfRecords
>propB : RecordOfRecords
>{...(foo && {foo})} : { foo: Record<never, never>; }
>(foo && {foo}) : { foo: Record<never, never>; }
>foo && {foo} : { foo: Record<never, never>; }
>foo : Record<never, never>
>{foo} : { foo: Record<never, never>; }
>foo : Record<never, never>
recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK
>recordOfRecords.propC = {...(foo !== undefined && {foo})} : { foo: Record<never, never>; } | {}
>recordOfRecords.propC : RecordOfRecords
>recordOfRecords : RecordOfRecords
>propC : RecordOfRecords
>{...(foo !== undefined && {foo})} : { foo: Record<never, never>; } | {}
>(foo !== undefined && {foo}) : false | { foo: Record<never, never>; }
>foo !== undefined && {foo} : false | { foo: Record<never, never>; }
>foo !== undefined : boolean
>foo : Record<never, never>
>undefined : undefined
>{foo} : { foo: Record<never, never>; }
>foo : Record<never, never>
interface RecordOfRecordsOrEmpty extends Record<keyof any, RecordOfRecordsOrEmpty | {}> {}
const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {}
>recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty
>{} : {}
recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK
>recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} : { foo: Record<never, never>; } | {}
>recordsOfRecordsOrEmpty.propA : {} | RecordOfRecordsOrEmpty
>recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty
>propA : {} | RecordOfRecordsOrEmpty
>{...(foo !== undefined ? {foo} : {})} : { foo: Record<never, never>; } | {}
>(foo !== undefined ? {foo} : {}) : { foo: Record<never, never>; } | {}
>foo !== undefined ? {foo} : {} : { foo: Record<never, never>; } | {}
>foo !== undefined : boolean
>foo : Record<never, never>
>undefined : undefined
>{foo} : { foo: Record<never, never>; }
>foo : Record<never, never>
>{} : {}
recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK
>recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} : { foo: Record<never, never>; }
>recordsOfRecordsOrEmpty.propB : {} | RecordOfRecordsOrEmpty
>recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty
>propB : {} | RecordOfRecordsOrEmpty
>{...(foo && {foo})} : { foo: Record<never, never>; }
>(foo && {foo}) : { foo: Record<never, never>; }
>foo && {foo} : { foo: Record<never, never>; }
>foo : Record<never, never>
>{foo} : { foo: Record<never, never>; }
>foo : Record<never, never>
recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK
>recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} : { foo: Record<never, never>; } | {}
>recordsOfRecordsOrEmpty.propC : {} | RecordOfRecordsOrEmpty
>recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty
>propC : {} | RecordOfRecordsOrEmpty
>{...(foo !== undefined && {foo})} : { foo: Record<never, never>; } | {}
>(foo !== undefined && {foo}) : false | { foo: Record<never, never>; }
>foo !== undefined && {foo} : false | { foo: Record<never, never>; }
>foo !== undefined : boolean
>foo : Record<never, never>
>undefined : undefined
>{foo} : { foo: Record<never, never>; }
>foo : Record<never, never>

View file

@ -0,0 +1,14 @@
// @strict: true
const foo: Record<never, never> = {} // OK
interface RecordOfRecords extends Record<keyof any, RecordOfRecords> {}
const recordOfRecords: RecordOfRecords = {}
recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK
recordOfRecords.propB = {...(foo && {foo})} // OK
recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK
interface RecordOfRecordsOrEmpty extends Record<keyof any, RecordOfRecordsOrEmpty | {}> {}
const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {}
recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK
recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK
recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK