Reputation: 24077
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
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
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
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
Upvotes: 3