TypeScript中如何限制方法泛型为对象且兼容interface与type?
解决方案
你可以通过Record<keyof T, unknown>作为泛型约束实现需求,完美兼容interface、type定义的对象类型,同时全程不引入any:
// 修改后的doSomething实现 function doSomething<T extends Record<keyof T, unknown>>(obj: T) { Object.entries(obj).forEach(([key, value]) => { console.log(key, value) }) } // 配套的自定义Hook也需要加上相同的泛型约束,避免入参不符合要求 function useCustomHook<T extends Record<keyof T, unknown>>(initialData: T[]) { const [changes, setChanges] = React.useState<T[]>([]) // 从changes中获取元素调用方法示例 changes.forEach(item => doSomething(item)) }
实现原理
- 自动过滤非对象类型:string、number、boolean、null、undefined等基本类型均不满足
Record<keyof T, unknown>的约束,传入时会直接抛出TS错误 - 兼容interface和type:
keyof T会自动推导目标类型的所有属性键,无论你用type还是interface定义对象类型,都不会出现「索引签名缺失」的报错 - 类型安全:全程没有使用any类型,所有属性值的类型都会被推导为unknown,符合严格类型校验要求
校验效果验证
你要求的异常调用场景都会触发TS报错:
doSomething('test') // 报错 doSomething(12) // 报错 doSomething(undefined) // 报错
合法调用场景均可正常通过校验:
// Type类型定义 type ExampleType = { property1: number, property2: string } // Interface定义 interface ExampleInterface { property1: number; property2: string; } doSomething({}) // 通过 doSomething({ property1: 12, property2: 'string'}) // 通过 doSomething<ExampleType>({ property1: 12, property2: 'string'}) // 通过 doSomething<ExampleInterface>({property1: 12, property2: 'string'}) // 通过
可选优化(禁止传入数组)
如果需要额外禁止数组类型的入参,可以把泛型约束调整为:
function doSomething<T extends Record<keyof T, unknown> & { length?: never }>(obj: T) { // 实现逻辑不变 }
该约束会过滤掉带length属性的数组类型,仅允许普通对象传入。
内容的提问来源于stack exchange,提问作者Martin Hofhanzl
相关产品推荐
相关产品推荐

