harryg
harryg

Reputation: 24077

In TypeScript, define a type for any object that has required properties

Say I have a function that takes some sort of object that has a name property. It doesn't care about the other properties on the object, only that it has a name of type string, thus it should accept any object type that conforms to this.

Is there a simple way to define such a type? The closest I've got is to extend a type constructed with Record but it seems a bit inelegant:

interface ThingWithName extends Record<string, any> {
    name: string
}

function greet(thing: ThingWithName) {
    return `hello, ${thing.name}`
}

greet({name: ''}) // fine
greet({name: '', age: 27}) // also fine

Upvotes: 0

Views: 1746

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249466

You don't need to do anything special for this. This will define the required property:

interface ThingWithName {
    name: string
}

function greet(thing: ThingWithName) {
    return `hello, ${thing.name}`
}

greet({name: ''}) // fine
let x = {name: '', age: 27};
greet(x) // also fine

Play

What you are probably hitting up against is excess property checks that prohibit extra properties when an object literal is directly assigned to a specific type reference. So in the code above this: greet({name: '', age: 27}) would be an error.

You can get around this limitation in one of several ways.

The safest one is to use a generic type parameter:

interface ThingWithName {
    name: string
}

function greet<T extends ThingWithName>(thing: T) {
    return `hello, ${thing.name} ${thing.age}` // age is invalid
}

greet({name: ''}) // fine
greet({name: '', age: 27}) // also fine

Play

If you want to be able to index into the object using a string then the solution you found of extending record is an ok one (same effect could have been achieved using an index signature):

interface ThingWithName extends Record<string, any> {
    name: string
}

function greet(thing: ThingWithName) {
    return `hello, ${thing.name} ${thing.age}` // age is valid, so be ware you can dot into thing with any prop 
}

greet({name: ''}) // fine
greet({ }) // err
greet({name: '', age: 27}) // also fine

Play

Upvotes: 3

Related Questions