Make a fresh empty object literal not a subtype of a type with an index signaure (#29975)

* Forbid inferable index checkign during subtype relationship checking

* Merge object.values and object.entries overloads to work around subtype change

* Invert subtype relationship between fresh empty objects and non-empty object types

* Remvoe comment

* Revert lib change

* Remove trailing whitespace ffs
This commit is contained in:
Wesley Wigham
2019-02-26 13:43:22 -08:00
committed by GitHub
parent 4db4c58924
commit 2533d8294e
11 changed files with 747 additions and 0 deletions

View File

@@ -12787,6 +12787,11 @@ namespace ts {
else if (isReadonlyArrayType(target) ? isArrayType(source) || isTupleType(source) : isArrayType(target) && isTupleType(source) && !source.target.readonly) {
return isRelatedTo(getIndexTypeOfType(source, IndexKind.Number) || anyType, getIndexTypeOfType(target, IndexKind.Number) || anyType, reportErrors);
}
// Consider a fresh empty object literal type "closed" under the subtype relationship - this way `{} <- {[idx: string]: any} <- fresh({})`
// and not `{} <- fresh({}) <- {[idx: string]: any}`
else if (relation === subtypeRelation && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
return Ternary.False;
}
// Even if relationship doesn't hold for unions, intersections, or generic type references,
// it may hold in a structural comparison.
// In a check of the form X = A & B, we will have previously checked if A relates to X or B relates

View File

@@ -0,0 +1,53 @@
tests/cases/compiler/emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts(41,3): error TS2322: Type 'Dictionary<string>' is not assignable to type 'Record<string, Bar>'.
Index signatures are incompatible.
Type 'string' is not assignable to type 'Bar'.
==== tests/cases/compiler/emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts (1 errors) ====
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts
// Begin types from Lodash.
interface Dictionary<T> {
[index: string]: T;
}
interface NumericDictionary<T> {
[index: number]: T;
}
type ObjectIterator<TObject, TResult> = (
value: TObject[keyof TObject],
key: string,
collection: TObject
) => TResult;
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
callback: DictionaryIterator<T, TResult>
): Dictionary<TResult> {
return null as any;
}
// End types from Lodash.
interface Foo {
foo: string;
}
interface Bar {
bar: string;
}
export function fooToBar(
foos: Record<string, Foo>
): Record<string, Bar | null> {
const result = foos == null ? {} : mapValues(foos, f => f.foo);
// This line _should_ fail, because `result` is not the right type.
return result;
~~~~~~~~~~~~~~
!!! error TS2322: Type 'Dictionary<string>' is not assignable to type 'Record<string, Bar>'.
!!! error TS2322: Index signatures are incompatible.
!!! error TS2322: Type 'string' is not assignable to type 'Bar'.
}

View File

@@ -0,0 +1,58 @@
//// [emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts]
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts
// Begin types from Lodash.
interface Dictionary<T> {
[index: string]: T;
}
interface NumericDictionary<T> {
[index: number]: T;
}
type ObjectIterator<TObject, TResult> = (
value: TObject[keyof TObject],
key: string,
collection: TObject
) => TResult;
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
callback: DictionaryIterator<T, TResult>
): Dictionary<TResult> {
return null as any;
}
// End types from Lodash.
interface Foo {
foo: string;
}
interface Bar {
bar: string;
}
export function fooToBar(
foos: Record<string, Foo>
): Record<string, Bar | null> {
const result = foos == null ? {} : mapValues(foos, f => f.foo);
// This line _should_ fail, because `result` is not the right type.
return result;
}
//// [emptyObjectNotSubtypeOfIndexSignatureContainingObject1.js]
"use strict";
exports.__esModule = true;
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues(obj, callback) {
return null;
}
function fooToBar(foos) {
var result = foos == null ? {} : mapValues(foos, function (f) { return f.foo; });
// This line _should_ fail, because `result` is not the right type.
return result;
}
exports.fooToBar = fooToBar;

View File

@@ -0,0 +1,118 @@
=== tests/cases/compiler/emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts ===
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts
// Begin types from Lodash.
interface Dictionary<T> {
>Dictionary : Symbol(Dictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 0, 0))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 2, 21))
[index: string]: T;
>index : Symbol(index, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 3, 3))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 2, 21))
}
interface NumericDictionary<T> {
>NumericDictionary : Symbol(NumericDictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 4, 1))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 6, 28))
[index: number]: T;
>index : Symbol(index, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 7, 3))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 6, 28))
}
type ObjectIterator<TObject, TResult> = (
>ObjectIterator : Symbol(ObjectIterator, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 8, 1))
>TObject : Symbol(TObject, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 10, 20))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 10, 28))
value: TObject[keyof TObject],
>value : Symbol(value, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 10, 41))
>TObject : Symbol(TObject, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 10, 20))
>TObject : Symbol(TObject, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 10, 20))
key: string,
>key : Symbol(key, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 11, 32))
collection: TObject
>collection : Symbol(collection, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 12, 14))
>TObject : Symbol(TObject, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 10, 20))
) => TResult;
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 10, 28))
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
>DictionaryIterator : Symbol(DictionaryIterator, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 14, 13))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 16, 24))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 16, 26))
>ObjectIterator : Symbol(ObjectIterator, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 8, 1))
>Dictionary : Symbol(Dictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 0, 0))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 16, 24))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 16, 26))
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
>mapValues : Symbol(mapValues, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 16, 77))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 19, 19))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 19, 21))
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
>obj : Symbol(obj, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 19, 31))
>Dictionary : Symbol(Dictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 0, 0))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 19, 19))
>NumericDictionary : Symbol(NumericDictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 4, 1))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 19, 19))
callback: DictionaryIterator<T, TResult>
>callback : Symbol(callback, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 20, 63))
>DictionaryIterator : Symbol(DictionaryIterator, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 14, 13))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 19, 19))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 19, 21))
): Dictionary<TResult> {
>Dictionary : Symbol(Dictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 0, 0))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 19, 21))
return null as any;
}
// End types from Lodash.
interface Foo {
>Foo : Symbol(Foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 24, 1))
foo: string;
>foo : Symbol(Foo.foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 27, 15))
}
interface Bar {
>Bar : Symbol(Bar, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 29, 1))
bar: string;
>bar : Symbol(Bar.bar, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 31, 15))
}
export function fooToBar(
>fooToBar : Symbol(fooToBar, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 33, 1))
foos: Record<string, Foo>
>foos : Symbol(foos, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 35, 25))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>Foo : Symbol(Foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 24, 1))
): Record<string, Bar | null> {
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>Bar : Symbol(Bar, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 29, 1))
const result = foos == null ? {} : mapValues(foos, f => f.foo);
>result : Symbol(result, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 38, 7))
>foos : Symbol(foos, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 35, 25))
>mapValues : Symbol(mapValues, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 16, 77))
>foos : Symbol(foos, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 35, 25))
>f : Symbol(f, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 38, 52))
>f.foo : Symbol(Foo.foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 27, 15))
>f : Symbol(f, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 38, 52))
>foo : Symbol(Foo.foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 27, 15))
// This line _should_ fail, because `result` is not the right type.
return result;
>result : Symbol(result, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts, 38, 7))
}

View File

@@ -0,0 +1,88 @@
=== tests/cases/compiler/emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts ===
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts
// Begin types from Lodash.
interface Dictionary<T> {
[index: string]: T;
>index : string
}
interface NumericDictionary<T> {
[index: number]: T;
>index : number
}
type ObjectIterator<TObject, TResult> = (
>ObjectIterator : ObjectIterator<TObject, TResult>
value: TObject[keyof TObject],
>value : TObject[keyof TObject]
key: string,
>key : string
collection: TObject
>collection : TObject
) => TResult;
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
>DictionaryIterator : ObjectIterator<Dictionary<T>, TResult>
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
>mapValues : <T, TResult>(obj: Dictionary<T> | NumericDictionary<T>, callback: ObjectIterator<Dictionary<T>, TResult>) => Dictionary<TResult>
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
>obj : Dictionary<T> | NumericDictionary<T>
>null : null
callback: DictionaryIterator<T, TResult>
>callback : ObjectIterator<Dictionary<T>, TResult>
): Dictionary<TResult> {
return null as any;
>null as any : any
>null : null
}
// End types from Lodash.
interface Foo {
foo: string;
>foo : string
}
interface Bar {
bar: string;
>bar : string
}
export function fooToBar(
>fooToBar : (foos: Record<string, Foo>) => Record<string, Bar>
foos: Record<string, Foo>
>foos : Record<string, Foo>
): Record<string, Bar | null> {
>null : null
const result = foos == null ? {} : mapValues(foos, f => f.foo);
>result : Dictionary<string>
>foos == null ? {} : mapValues(foos, f => f.foo) : Dictionary<string>
>foos == null : boolean
>foos : Record<string, Foo>
>null : null
>{} : {}
>mapValues(foos, f => f.foo) : Dictionary<string>
>mapValues : <T, TResult>(obj: Dictionary<T> | NumericDictionary<T>, callback: ObjectIterator<Dictionary<T>, TResult>) => Dictionary<TResult>
>foos : Record<string, Foo>
>f => f.foo : (f: Foo) => string
>f : Foo
>f.foo : string
>f : Foo
>foo : string
// This line _should_ fail, because `result` is not the right type.
return result;
>result : Dictionary<string>
}

View File

@@ -0,0 +1,54 @@
tests/cases/compiler/emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts(42,3): error TS2322: Type 'Dictionary<string>' is not assignable to type 'Record<string, Bar>'.
Index signatures are incompatible.
Type 'string' is not assignable to type 'Bar'.
==== tests/cases/compiler/emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts (1 errors) ====
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts
// Begin types from Lodash.
interface Dictionary<T> {
[index: string]: T;
}
interface NumericDictionary<T> {
[index: number]: T;
}
type ObjectIterator<TObject, TResult> = (
value: TObject[keyof TObject],
key: string,
collection: TObject
) => TResult;
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
callback: DictionaryIterator<T, TResult>
): Dictionary<TResult> {
return null as any;
}
// End types from Lodash.
interface Foo {
foo: string;
}
interface Bar {
bar: string;
}
export function fooToBar(
foos: Record<string, Foo>
): Record<string, Bar | null> {
const wat = mapValues(foos, f => f.foo);
const result = foos == null ? {} : mapValues(foos, f => f.foo);
// This line _should_ fail, because `result` is not the right type.
return result;
~~~~~~~~~~~~~~
!!! error TS2322: Type 'Dictionary<string>' is not assignable to type 'Record<string, Bar>'.
!!! error TS2322: Index signatures are incompatible.
!!! error TS2322: Type 'string' is not assignable to type 'Bar'.
}

View File

@@ -0,0 +1,60 @@
//// [emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts]
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts
// Begin types from Lodash.
interface Dictionary<T> {
[index: string]: T;
}
interface NumericDictionary<T> {
[index: number]: T;
}
type ObjectIterator<TObject, TResult> = (
value: TObject[keyof TObject],
key: string,
collection: TObject
) => TResult;
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
callback: DictionaryIterator<T, TResult>
): Dictionary<TResult> {
return null as any;
}
// End types from Lodash.
interface Foo {
foo: string;
}
interface Bar {
bar: string;
}
export function fooToBar(
foos: Record<string, Foo>
): Record<string, Bar | null> {
const wat = mapValues(foos, f => f.foo);
const result = foos == null ? {} : mapValues(foos, f => f.foo);
// This line _should_ fail, because `result` is not the right type.
return result;
}
//// [emptyObjectNotSubtypeOfIndexSignatureContainingObject2.js]
"use strict";
exports.__esModule = true;
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues(obj, callback) {
return null;
}
function fooToBar(foos) {
var wat = mapValues(foos, function (f) { return f.foo; });
var result = foos == null ? {} : mapValues(foos, function (f) { return f.foo; });
// This line _should_ fail, because `result` is not the right type.
return result;
}
exports.fooToBar = fooToBar;

View File

@@ -0,0 +1,127 @@
=== tests/cases/compiler/emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts ===
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts
// Begin types from Lodash.
interface Dictionary<T> {
>Dictionary : Symbol(Dictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 0, 0))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 2, 21))
[index: string]: T;
>index : Symbol(index, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 3, 3))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 2, 21))
}
interface NumericDictionary<T> {
>NumericDictionary : Symbol(NumericDictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 4, 1))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 6, 28))
[index: number]: T;
>index : Symbol(index, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 7, 3))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 6, 28))
}
type ObjectIterator<TObject, TResult> = (
>ObjectIterator : Symbol(ObjectIterator, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 8, 1))
>TObject : Symbol(TObject, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 10, 20))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 10, 28))
value: TObject[keyof TObject],
>value : Symbol(value, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 10, 41))
>TObject : Symbol(TObject, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 10, 20))
>TObject : Symbol(TObject, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 10, 20))
key: string,
>key : Symbol(key, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 11, 32))
collection: TObject
>collection : Symbol(collection, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 12, 14))
>TObject : Symbol(TObject, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 10, 20))
) => TResult;
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 10, 28))
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
>DictionaryIterator : Symbol(DictionaryIterator, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 14, 13))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 16, 24))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 16, 26))
>ObjectIterator : Symbol(ObjectIterator, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 8, 1))
>Dictionary : Symbol(Dictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 0, 0))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 16, 24))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 16, 26))
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
>mapValues : Symbol(mapValues, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 16, 77))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 19, 19))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 19, 21))
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
>obj : Symbol(obj, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 19, 31))
>Dictionary : Symbol(Dictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 0, 0))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 19, 19))
>NumericDictionary : Symbol(NumericDictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 4, 1))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 19, 19))
callback: DictionaryIterator<T, TResult>
>callback : Symbol(callback, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 20, 63))
>DictionaryIterator : Symbol(DictionaryIterator, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 14, 13))
>T : Symbol(T, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 19, 19))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 19, 21))
): Dictionary<TResult> {
>Dictionary : Symbol(Dictionary, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 0, 0))
>TResult : Symbol(TResult, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 19, 21))
return null as any;
}
// End types from Lodash.
interface Foo {
>Foo : Symbol(Foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 24, 1))
foo: string;
>foo : Symbol(Foo.foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 27, 15))
}
interface Bar {
>Bar : Symbol(Bar, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 29, 1))
bar: string;
>bar : Symbol(Bar.bar, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 31, 15))
}
export function fooToBar(
>fooToBar : Symbol(fooToBar, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 33, 1))
foos: Record<string, Foo>
>foos : Symbol(foos, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 35, 25))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>Foo : Symbol(Foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 24, 1))
): Record<string, Bar | null> {
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>Bar : Symbol(Bar, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 29, 1))
const wat = mapValues(foos, f => f.foo);
>wat : Symbol(wat, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 38, 7))
>mapValues : Symbol(mapValues, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 16, 77))
>foos : Symbol(foos, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 35, 25))
>f : Symbol(f, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 38, 29))
>f.foo : Symbol(Foo.foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 27, 15))
>f : Symbol(f, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 38, 29))
>foo : Symbol(Foo.foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 27, 15))
const result = foos == null ? {} : mapValues(foos, f => f.foo);
>result : Symbol(result, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 39, 7))
>foos : Symbol(foos, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 35, 25))
>mapValues : Symbol(mapValues, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 16, 77))
>foos : Symbol(foos, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 35, 25))
>f : Symbol(f, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 39, 52))
>f.foo : Symbol(Foo.foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 27, 15))
>f : Symbol(f, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 39, 52))
>foo : Symbol(Foo.foo, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 27, 15))
// This line _should_ fail, because `result` is not the right type.
return result;
>result : Symbol(result, Decl(emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts, 39, 7))
}

View File

@@ -0,0 +1,99 @@
=== tests/cases/compiler/emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts ===
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts
// Begin types from Lodash.
interface Dictionary<T> {
[index: string]: T;
>index : string
}
interface NumericDictionary<T> {
[index: number]: T;
>index : number
}
type ObjectIterator<TObject, TResult> = (
>ObjectIterator : ObjectIterator<TObject, TResult>
value: TObject[keyof TObject],
>value : TObject[keyof TObject]
key: string,
>key : string
collection: TObject
>collection : TObject
) => TResult;
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
>DictionaryIterator : ObjectIterator<Dictionary<T>, TResult>
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
>mapValues : <T, TResult>(obj: Dictionary<T> | NumericDictionary<T>, callback: ObjectIterator<Dictionary<T>, TResult>) => Dictionary<TResult>
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
>obj : Dictionary<T> | NumericDictionary<T>
>null : null
callback: DictionaryIterator<T, TResult>
>callback : ObjectIterator<Dictionary<T>, TResult>
): Dictionary<TResult> {
return null as any;
>null as any : any
>null : null
}
// End types from Lodash.
interface Foo {
foo: string;
>foo : string
}
interface Bar {
bar: string;
>bar : string
}
export function fooToBar(
>fooToBar : (foos: Record<string, Foo>) => Record<string, Bar>
foos: Record<string, Foo>
>foos : Record<string, Foo>
): Record<string, Bar | null> {
>null : null
const wat = mapValues(foos, f => f.foo);
>wat : Dictionary<string>
>mapValues(foos, f => f.foo) : Dictionary<string>
>mapValues : <T, TResult>(obj: Dictionary<T> | NumericDictionary<T>, callback: ObjectIterator<Dictionary<T>, TResult>) => Dictionary<TResult>
>foos : Record<string, Foo>
>f => f.foo : (f: Foo) => string
>f : Foo
>f.foo : string
>f : Foo
>foo : string
const result = foos == null ? {} : mapValues(foos, f => f.foo);
>result : Dictionary<string>
>foos == null ? {} : mapValues(foos, f => f.foo) : Dictionary<string>
>foos == null : boolean
>foos : Record<string, Foo>
>null : null
>{} : {}
>mapValues(foos, f => f.foo) : Dictionary<string>
>mapValues : <T, TResult>(obj: Dictionary<T> | NumericDictionary<T>, callback: ObjectIterator<Dictionary<T>, TResult>) => Dictionary<TResult>
>foos : Record<string, Foo>
>f => f.foo : (f: Foo) => string
>f : Foo
>f.foo : string
>f : Foo
>foo : string
// This line _should_ fail, because `result` is not the right type.
return result;
>result : Dictionary<string>
}

View File

@@ -0,0 +1,42 @@
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject2.ts
// Begin types from Lodash.
interface Dictionary<T> {
[index: string]: T;
}
interface NumericDictionary<T> {
[index: number]: T;
}
type ObjectIterator<TObject, TResult> = (
value: TObject[keyof TObject],
key: string,
collection: TObject
) => TResult;
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
callback: DictionaryIterator<T, TResult>
): Dictionary<TResult> {
return null as any;
}
// End types from Lodash.
interface Foo {
foo: string;
}
interface Bar {
bar: string;
}
export function fooToBar(
foos: Record<string, Foo>
): Record<string, Bar | null> {
const result = foos == null ? {} : mapValues(foos, f => f.foo);
// This line _should_ fail, because `result` is not the right type.
return result;
}

View File

@@ -0,0 +1,43 @@
// This should behave the same as emptyObjectNotSubtypeOfIndexSignatureContainingObject1.ts
// Begin types from Lodash.
interface Dictionary<T> {
[index: string]: T;
}
interface NumericDictionary<T> {
[index: number]: T;
}
type ObjectIterator<TObject, TResult> = (
value: TObject[keyof TObject],
key: string,
collection: TObject
) => TResult;
type DictionaryIterator<T, TResult> = ObjectIterator<Dictionary<T>, TResult>;
// In lodash.d.ts this function has many overloads, but this seems to be the problematic one.
function mapValues<T, TResult>(
obj: Dictionary<T> | NumericDictionary<T> | null | undefined,
callback: DictionaryIterator<T, TResult>
): Dictionary<TResult> {
return null as any;
}
// End types from Lodash.
interface Foo {
foo: string;
}
interface Bar {
bar: string;
}
export function fooToBar(
foos: Record<string, Foo>
): Record<string, Bar | null> {
const wat = mapValues(foos, f => f.foo);
const result = foos == null ? {} : mapValues(foos, f => f.foo);
// This line _should_ fail, because `result` is not the right type.
return result;
}