The below code fails type checking with:
Object literal may only specify known properties
type A = {a: number}
const a:A = {a:42, b: 43};
Why then does the following succeed:
type A = {}
const a:A = {a:42, b: 43};
What's the intuition here? What is the type that only accepts objects with value {}
? Clearly the type {}
accepts a lot more.
CodePudding user response:
TypeScript's type system is structural and supports structural subtyping, so object types are open in the sense that they do not prohibit extra unknown properties. They are not "sealed" or "exact" (as requested in microsoft/TypeScript#12936):
type A = { a: number }
const a2 = { a: 42, b: 43 };
const a3: A = a2; // no error
This supports interface and class extension; without it, interface and class hierarchies like interface X extends Y { extraProp: any }
or class X extends Y { extraProp: any }
would not form type hierarchies and things would be difficult to reason about.
However, as you noticed, there are some specific situations in which the compiler warns you about unexpected extra properties. This excess property checking implemented in microsoft/TypeScript#3823 only kicks in when you are assigning an object literal (that is, an object initializer value with curly braces) directly to a non-empty target. See this comment in microsoft/TypeScript#14606 for authoritative corroboration).
In your examples, the type {a: number}
is non-empty, so the assignment of the object literal {a: 42, b: 43}
to a
produces a warning. But the type {}
is empty, so the assignment of the same object literal to a
produces no warning.
Before the introduction of the unknown
type the empty object type {}
was used everywhere to mean "any non-nullish type whatsoever" and having the compiler complain for this would have introduced a lot of unhelpful warnings.
In any case, you can think of excess property checking more as a linter rule than a type system feature.
So that's the answer for why this is happening.
To your last question: "What is the type that only accepts objects with value {}
?" Well, as I said, the type system is not geared toward prohibiting extra properties. There are workarounds like this one in microsoft/TypeScript#12936 using generics to check for excess properties and constrain them to the never
type, but they are complicated .
In the particular case of {}
, you can use an index signature with value type never
to prohibit all properties:
type A = { [k: string]: never };
const a: A = { a: 42, b: 43 }; // error
const a2: A = { a: 42 }; // error
const a3: A = {}; // okay
I'm not sure what the use case for such a type is, but you might find it harder to use than you'd like.