Reputation: 15336
I want to define an interface for a general Table Row, basically it's any object, limited to attribute values as primitive types.
But when I try to assign an object of specific type to row it fails. Why? And how to make it work? I want to enforce that rows could be only objects with primitive attribute values.
// Table row
export type Row = Record<string, string | number | undefined>
// User
interface User { name: string }
const jim: User = { name: 'Jim' }
const row: Row = jim // <== Error
Error
Type 'User' is not assignable to type 'Row'.
Index signature for type 'string' is missing in type 'User'.
Upvotes: 0
Views: 206
Reputation: 95614
Interfaces can be extended. Without an index type restricting it, an object that extends User that can be stored in a User-typed variable could have other properties.
// User
interface User { name: string }
interface FooUser extends User { foo(): void; }
const jim: FooUser = { name: 'Jim', foo() {} }
const jimAsUser: User = jim;
const row: Row = jim; // fails as it should:
// foo is not a string, number, or undefined!
Note that if you use type
rather than interface
, it is safer: Extra properties in subinterfaces are not expected. That said, Typescript does allow this, even though it shouldn't. type
and interface
have some subtle details, which the TypeScript handbook alludes to (though without getting into this particular case).
// UserAsType
type UserAsType = { name: string };
const jimAsType: UserAsType = { name: 'Jim' }
const jimAsTypeFromFooUser: UserAsType = jim; // this *should* fail
const row2: Row = jimAsType
Upvotes: 1