Description
Bug Report
🔎 Search Terms
- interface type
- inconsistent interface type extends
🕗 Version & Regression Information
- This is the behavior in every version I tried*, and I reviewed the FAQ for entries about missing index signatures and type assignment.
*Checked 4.1.3, 4.1.5, current beta, nightly, and some previous versions.
⏯ Playground Link
Playground link with relevant code
💻 Code
type TFoo = {
type: string;
value: number;
};
interface IFoo {
type: string;
value: number;
}
// Just to check if "interface-ness" is carried over through type assignment.
type ITFoo = IFoo;
interface AnyObject {
[key: string]: unknown;
}
type AnyObjectType = {
[key: string]: unknown;
};
type TypeTest = TFoo extends Record<PropertyKey, unknown> ? true : false; // => true
type InterfaceTest = IFoo extends Record<PropertyKey, unknown> ? true : false; // => false (expected 'true')
type InterfaceTypeTest = ITFoo extends Record<PropertyKey, unknown> ? true : false; // => false (expected 'true')
type TypeTest2 = TFoo extends { [key: string]: unknown } ? true : false; // => true
type InterfaceTest2 = IFoo extends { [key: string]: unknown } ? true : false; // => false (expected 'true')
type InterfaceTypeTest2 = ITFoo extends { [key: string]: unknown } ? true : false; // => false (expected 'true')
type TypeTest3 = TFoo extends AnyObject ? true : false; // => true
type InterfaceTest3 = IFoo extends AnyObject ? true : false; // => false (expected 'true')
type InterfaceTypeTest3 = ITFoo extends AnyObject ? true : false; // => false (expected 'true')
type TypeTest4 = TFoo extends AnyObjectType ? true : false; // => true
type InterfaceTest4 = IFoo extends AnyObjectType ? true : false; // => false (expected 'true')
type InterfaceTypeTest4 = ITFoo extends AnyObjectType ? true : false; // => false (expected 'true')
function checkObject(obj: Record<PropertyKey, unknown>) {}
const fooType: TFoo = {
type: "misc",
value: 0
};
const fooInterface: IFoo = {
type: "number",
value: 20
};
const fooInterfaceType: ITFoo = {
type: "number",
value: 30
};
checkObject(fooType);
checkObject(fooInterface); // type error
checkObject(fooInterfaceType); // type error
🙁 Actual behavior
InterfaceTest
and InterfaceTypeTest
(all variations) are assigned the type of false
, while the various TypeTests
all return true
.
Additionally, fooInterface
and fooInterfaceType
fails type check when passed to the stub function checkObject()
with the error code 2345 (missing index signature).
🙂 Expected behavior
TypeTest
, InterfaceTest
, and InterfaceTypeTest
(all variations) should be assigned the type true
, and the three usages of the function checkObject()
passes type check.
My understanding of unknown
is that every type extends unknown
(like any
) and unknown
extends only unknown
, so object types of any shape should extend Record<PropertyKey, unknown>
. From this is why I think the correct result is that all 3 variants of Foo
extends Record<PropertyKey, unknown>
.
Also, even if that assumption is incorrect, the types TFoo
and IFoo
only differ in that they're declared as a type
or an interface
. I expect that this means that TFoo
and IFoo
should be treated identically by various type constraints.
I initially encountered this issue when I was trying to pass an object with an interface-declared type to a function in the Deno standard library (specifically assertObjectMatch()
under "testing/asserts" which accepts two arguments of type Record<PropertyKey, unknown>
).