I have a function that I would like to accept any interface that contains only values of type T
. For example:
// library function
function print(thing: /* any interface that is boolean | string */) {
console.log(thing)
}
// user defined interface
interface Machine {
powered: boolean
}
// user defined interface
interface Animal {
sound: string
}
// user defined interface
interface Person {
age: number
}
let blender: Machine = {
powered: false
}
let cat: Animal = {
sound: "meow"
}
let person: Person = {
age: 24,
}
print(blender); // all good
print(cat); // all good
print(person); // nope
I tried a mapped type (and Record<string, T>
), but it appears that Typescript doesn't let you cast interfaces to those. How should I type thing
to accept any interface with values of type boolean | string
?
CodePudding user response:
If the function parameter thing
type is Record<string, T>
, it can accept only arguments that extend { [k: string]: T }
, i.e. with at least string
index signature, which is not included in well-defined interfaces like Animal
or Machine
.
To accept any kind of interface with some unknown but well-defined keys (i.e. which may not have an index signature), you can use a generic parameter type that "extends its own keys":
function print<T extends { [K in keyof T]: string | boolean }>(thing: T) {
console.log(thing)
}
print(blender); // all good
print(cat); // all good
print(person); // nope Error: Type 'number' is not assignable to type 'string | boolean'.
const rec: Record<string, string> = {}
print(rec); // Also okay
Another possibility would be to use the generic parameter type only to infer the passed argument keys:
function print<T>(thing: Record<keyof T , string | boolean>) {
console.log(thing)
}