Alex Craft
Alex Craft

Reputation: 15336

Specific Object Type can't be typed as Record?

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.

playground

// 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

Answers (1)

Jeff Bowman
Jeff Bowman

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

Playground Link

Upvotes: 1

Related Questions