diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 106142711d946..c1246f04e842a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21218,19 +21218,31 @@ namespace ts { } return literalTypesWithSameBaseType(types) ? getUnionType(types) : - reduceLeft(types, (s, t) => isTypeSubtypeOf(s, t) ? t : s)!; + reduceLeft(types, (s, t) => isTypeSubtypeOf(getNonNullableTypeIfNeeded(s), getNonNullableTypeIfNeeded(t)) ? t : s)!; } function getCommonSupertype(types: Type[]): Type { if (!strictNullChecks) { return getSupertypeOrUnion(types); } - const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable)); - if (primaryTypes.length) { - const supertypeOrUnion = getSupertypeOrUnion(primaryTypes); - return primaryTypes === types ? supertypeOrUnion : getUnionType([supertypeOrUnion, ...filter(types, t => !!(t.flags & TypeFlags.Nullable))]); + + const supertypeOrUnion = getSupertypeOrUnion(types); + + const shouldHaveNull = some(types, t => someHas(t, TypeFlags.Null)); + const shouldHaveUndefined = some(types, t => someHas(t, TypeFlags.Undefined)); + + let missingNullableFlags: TypeFlags = 0; + if (shouldHaveNull && !someHas(supertypeOrUnion, TypeFlags.Null)) { + missingNullableFlags |= TypeFlags.Null; + } + if (shouldHaveUndefined && !someHas(supertypeOrUnion, TypeFlags.Undefined)) { + missingNullableFlags |= TypeFlags.Undefined; + } + return getNullableType(supertypeOrUnion, missingNullableFlags); + + function someHas(type: Type, flags: TypeFlags): boolean { + return someType(type, t => !!(t.flags & flags)); } - return getUnionType(types, UnionReduction.Subtype); } // Return the leftmost type for which no type to the right is a subtype. diff --git a/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.js b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.js new file mode 100644 index 0000000000000..5323e2dfe5585 --- /dev/null +++ b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.js @@ -0,0 +1,55 @@ +//// [inferenceDoesNotAddUndefinedOrNull.ts] +interface NodeArray extends ReadonlyArray {} + +interface Node { + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; +} + +declare function toArray(value: T | T[]): T[]; +declare function toArray(value: T | readonly T[]): readonly T[]; + +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== undefined) { + result.push(...toArray(value)); + } + }); + return result; +} + +function flatMapChildren2(node: Node, cb: (child: Node) => readonly T[] | T | null): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== null) { + result.push(...toArray(value)); + } + }); + return result; +} + + +//// [inferenceDoesNotAddUndefinedOrNull.js] +"use strict"; +function flatMapChildren(node, cb) { + var result = []; + node.forEachChild(function (child) { + var value = cb(child); + if (value !== undefined) { + result.push.apply(result, toArray(value)); + } + }); + return result; +} +function flatMapChildren2(node, cb) { + var result = []; + node.forEachChild(function (child) { + var value = cb(child); + if (value !== null) { + result.push.apply(result, toArray(value)); + } + }); + return result; +} diff --git a/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.symbols b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.symbols new file mode 100644 index 0000000000000..96d07b95cc00b --- /dev/null +++ b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.symbols @@ -0,0 +1,127 @@ +=== tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts === +interface NodeArray extends ReadonlyArray {} +>NodeArray : Symbol(NodeArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 0)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 20)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 20)) + +interface Node { +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) + + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; +>forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 17)) +>cbNode : Symbol(cbNode, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 20)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 29)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 17)) +>cbNodeArray : Symbol(cbNodeArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 58)) +>nodes : Symbol(nodes, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 74)) +>NodeArray : Symbol(NodeArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 0)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 17)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 3, 17)) +} + +declare function toArray(value: T | T[]): T[]; +>toArray : Symbol(toArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 4, 1), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 49)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 25)) +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 28)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 25)) + +declare function toArray(value: T | readonly T[]): readonly T[]; +>toArray : Symbol(toArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 4, 1), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 49)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 25)) +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 28)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 25)) + +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { +>flatMapChildren : Symbol(flatMapChildren, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 7, 67)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 28)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>cb : Symbol(cb, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 39)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 45)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) + + const result: T[] = []; +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 10, 9)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 25)) + + node.forEachChild(child => { +>node.forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 28)) +>forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 11, 22)) + + const value = cb(child); +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 12, 13)) +>cb : Symbol(cb, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 9, 39)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 11, 22)) + + if (value !== undefined) { +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 12, 13)) +>undefined : Symbol(undefined) + + result.push(...toArray(value)); +>result.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 10, 9)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>toArray : Symbol(toArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 4, 1), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 49)) +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 12, 13)) + } + }); + return result; +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 10, 9)) +} + +function flatMapChildren2(node: Node, cb: (child: Node) => readonly T[] | T | null): readonly T[] { +>flatMapChildren2 : Symbol(flatMapChildren2, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 18, 1)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 29)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>cb : Symbol(cb, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 40)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 46)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 0, 63)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) + + const result: T[] = []; +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 21, 9)) +>T : Symbol(T, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 26)) + + node.forEachChild(child => { +>node.forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>node : Symbol(node, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 29)) +>forEachChild : Symbol(Node.forEachChild, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 2, 16)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 22, 22)) + + const value = cb(child); +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 23, 13)) +>cb : Symbol(cb, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 20, 40)) +>child : Symbol(child, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 22, 22)) + + if (value !== null) { +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 23, 13)) + + result.push(...toArray(value)); +>result.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 21, 9)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>toArray : Symbol(toArray, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 4, 1), Decl(inferenceDoesNotAddUndefinedOrNull.ts, 6, 49)) +>value : Symbol(value, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 23, 13)) + } + }); + return result; +>result : Symbol(result, Decl(inferenceDoesNotAddUndefinedOrNull.ts, 21, 9)) +} + diff --git a/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.types b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.types new file mode 100644 index 0000000000000..a36bafa23e748 --- /dev/null +++ b/tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.types @@ -0,0 +1,109 @@ +=== tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts === +interface NodeArray extends ReadonlyArray {} + +interface Node { + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; +>forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>cbNode : (node: Node) => T | undefined +>node : Node +>cbNodeArray : ((nodes: NodeArray) => T | undefined) | undefined +>nodes : NodeArray +} + +declare function toArray(value: T | T[]): T[]; +>toArray : { (value: T | T[]): T[]; (value: T | readonly T[]): readonly T[]; } +>value : T | T[] + +declare function toArray(value: T | readonly T[]): readonly T[]; +>toArray : { (value: T | T[]): T[]; (value: T | readonly T[]): readonly T[]; } +>value : T | readonly T[] + +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { +>flatMapChildren : (node: Node, cb: (child: Node) => readonly T[] | T | undefined) => readonly T[] +>node : Node +>cb : (child: Node) => readonly T[] | T | undefined +>child : Node + + const result: T[] = []; +>result : T[] +>[] : never[] + + node.forEachChild(child => { +>node.forEachChild(child => { const value = cb(child); if (value !== undefined) { result.push(...toArray(value)); } }) : void | undefined +>node.forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>node : Node +>forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>child => { const value = cb(child); if (value !== undefined) { result.push(...toArray(value)); } } : (child: Node) => void +>child : Node + + const value = cb(child); +>value : T | readonly T[] | undefined +>cb(child) : T | readonly T[] | undefined +>cb : (child: Node) => T | readonly T[] | undefined +>child : Node + + if (value !== undefined) { +>value !== undefined : boolean +>value : T | readonly T[] | undefined +>undefined : undefined + + result.push(...toArray(value)); +>result.push(...toArray(value)) : number +>result.push : (...items: T[]) => number +>result : T[] +>push : (...items: T[]) => number +>...toArray(value) : T +>toArray(value) : readonly T[] +>toArray : { (value: T | T[]): T[]; (value: T | readonly T[]): readonly T[]; } +>value : readonly T[] | (T & ({} | null)) + } + }); + return result; +>result : T[] +} + +function flatMapChildren2(node: Node, cb: (child: Node) => readonly T[] | T | null): readonly T[] { +>flatMapChildren2 : (node: Node, cb: (child: Node) => readonly T[] | T | null) => readonly T[] +>node : Node +>cb : (child: Node) => readonly T[] | T | null +>child : Node +>null : null + + const result: T[] = []; +>result : T[] +>[] : never[] + + node.forEachChild(child => { +>node.forEachChild(child => { const value = cb(child); if (value !== null) { result.push(...toArray(value)); } }) : void | undefined +>node.forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>node : Node +>forEachChild : (cbNode: (node: Node) => T | undefined, cbNodeArray?: ((nodes: NodeArray) => T | undefined) | undefined) => T | undefined +>child => { const value = cb(child); if (value !== null) { result.push(...toArray(value)); } } : (child: Node) => void +>child : Node + + const value = cb(child); +>value : T | readonly T[] | null +>cb(child) : T | readonly T[] | null +>cb : (child: Node) => T | readonly T[] | null +>child : Node + + if (value !== null) { +>value !== null : boolean +>value : T | readonly T[] | null +>null : null + + result.push(...toArray(value)); +>result.push(...toArray(value)) : number +>result.push : (...items: T[]) => number +>result : T[] +>push : (...items: T[]) => number +>...toArray(value) : T +>toArray(value) : readonly T[] +>toArray : { (value: T | T[]): T[]; (value: T | readonly T[]): readonly T[]; } +>value : readonly T[] | (T & ({} | undefined)) + } + }); + return result; +>result : T[] +} + diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.js b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.js new file mode 100644 index 0000000000000..9725e22509f84 --- /dev/null +++ b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.js @@ -0,0 +1,40 @@ +//// [inferenceOfNullableObjectTypesWithCommonBase.ts] +function equal(a: T, b: T) { } + +let v = null!; + +// Object types with common base types + +type B = { foo: string } +type D = { foo: string; bar: number } + +equal(v as B, v as undefined | D) +equal(v as undefined | D, v as B) + +equal(v as B, v as undefined | D) +equal(v as undefined | D, v as B) + +equal(v as B, v as undefined) +equal(v as undefined, v as B) + +equal(v as B, v as D) +equal(v as D, v as B) + +equal(v as B, v as B | undefined) +equal(v as B | undefined, v as B) + + +//// [inferenceOfNullableObjectTypesWithCommonBase.js] +"use strict"; +function equal(a, b) { } +var v = null; +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); +equal(v, v); diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.symbols b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.symbols new file mode 100644 index 0000000000000..fe614f605469f --- /dev/null +++ b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.symbols @@ -0,0 +1,93 @@ +=== tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts === +function equal(a: T, b: T) { } +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>T : Symbol(T, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 15)) +>a : Symbol(a, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 18)) +>T : Symbol(T, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 15)) +>b : Symbol(b, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 23)) +>T : Symbol(T, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 15)) + +let v = null!; +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) + +// Object types with common base types + +type B = { foo: string } +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>foo : Symbol(foo, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 10)) + +type D = { foo: string; bar: number } +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) +>foo : Symbol(foo, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 7, 10)) +>bar : Symbol(bar, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 7, 23)) + +equal(v as B, v as undefined | D) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) + +equal(v as undefined | D, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B, v as undefined | D) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) + +equal(v as undefined | D, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B, v as undefined) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) + +equal(v as undefined, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B, v as D) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) + +equal(v as D, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>D : Symbol(D, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 6, 24)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B, v as B | undefined) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + +equal(v as B | undefined, v as B) +>equal : Symbol(equal, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 0, 0)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) +>v : Symbol(v, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 3)) +>B : Symbol(B, Decl(inferenceOfNullableObjectTypesWithCommonBase.ts, 2, 14)) + diff --git a/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.types b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.types new file mode 100644 index 0000000000000..f78a78e838182 --- /dev/null +++ b/tests/baselines/reference/inferenceOfNullableObjectTypesWithCommonBase.types @@ -0,0 +1,102 @@ +=== tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts === +function equal(a: T, b: T) { } +>equal : (a: T, b: T) => void +>a : T +>b : T + +let v = null!; +>v : never +>null! : never +>null : null + +// Object types with common base types + +type B = { foo: string } +>B : { foo: string; } +>foo : string + +type D = { foo: string; bar: number } +>D : { foo: string; bar: number; } +>foo : string +>bar : number + +equal(v as B, v as undefined | D) +>equal(v as B, v as undefined | D) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as undefined | D : D | undefined +>v : never + +equal(v as undefined | D, v as B) +>equal(v as undefined | D, v as B) : void +>equal : (a: T, b: T) => void +>v as undefined | D : D | undefined +>v : never +>v as B : B +>v : never + +equal(v as B, v as undefined | D) +>equal(v as B, v as undefined | D) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as undefined | D : D | undefined +>v : never + +equal(v as undefined | D, v as B) +>equal(v as undefined | D, v as B) : void +>equal : (a: T, b: T) => void +>v as undefined | D : D | undefined +>v : never +>v as B : B +>v : never + +equal(v as B, v as undefined) +>equal(v as B, v as undefined) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as undefined : undefined +>v : never + +equal(v as undefined, v as B) +>equal(v as undefined, v as B) : void +>equal : (a: T, b: T) => void +>v as undefined : undefined +>v : never +>v as B : B +>v : never + +equal(v as B, v as D) +>equal(v as B, v as D) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as D : D +>v : never + +equal(v as D, v as B) +>equal(v as D, v as B) : void +>equal : (a: T, b: T) => void +>v as D : D +>v : never +>v as B : B +>v : never + +equal(v as B, v as B | undefined) +>equal(v as B, v as B | undefined) : void +>equal : (a: T, b: T) => void +>v as B : B +>v : never +>v as B | undefined : B | undefined +>v : never + +equal(v as B | undefined, v as B) +>equal(v as B | undefined, v as B) : void +>equal : (a: T, b: T) => void +>v as B | undefined : B | undefined +>v : never +>v as B : B +>v : never + diff --git a/tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts b/tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts new file mode 100644 index 0000000000000..75f53691a2511 --- /dev/null +++ b/tests/cases/compiler/inferenceDoesNotAddUndefinedOrNull.ts @@ -0,0 +1,32 @@ +// @strict: true + +interface NodeArray extends ReadonlyArray {} + +interface Node { + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; +} + +declare function toArray(value: T | T[]): T[]; +declare function toArray(value: T | readonly T[]): readonly T[]; + +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== undefined) { + result.push(...toArray(value)); + } + }); + return result; +} + +function flatMapChildren2(node: Node, cb: (child: Node) => readonly T[] | T | null): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== null) { + result.push(...toArray(value)); + } + }); + return result; +} diff --git a/tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts b/tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts new file mode 100644 index 0000000000000..950629a7b7f72 --- /dev/null +++ b/tests/cases/compiler/inferenceOfNullableObjectTypesWithCommonBase.ts @@ -0,0 +1,25 @@ +// @strict: true + +function equal(a: T, b: T) { } + +let v = null!; + +// Object types with common base types + +type B = { foo: string } +type D = { foo: string; bar: number } + +equal(v as B, v as undefined | D) +equal(v as undefined | D, v as B) + +equal(v as B, v as undefined | D) +equal(v as undefined | D, v as B) + +equal(v as B, v as undefined) +equal(v as undefined, v as B) + +equal(v as B, v as D) +equal(v as D, v as B) + +equal(v as B, v as B | undefined) +equal(v as B | undefined, v as B)