TypeScript使用泛型接收两类对象参数报属性不存在如何解决
这是一个看似简单的使用场景,但暂未找到覆盖该场景的相关示例。
需求是实现一个支持接收两种结构高度相似的不同对象作为入参的函数,最初计划使用泛型实现,初始代码如下:
const myFunction = <MyGenericType>(arrayOfObjects: MyGenericType[]) => { return arrayOfObjects.map((obj: MyGenericType) => ({ id: obj.id, name: obj.name, someProperty: obj.someProperty || obj.someOtherProperty, })) }
函数允许传入的参数为以下两种类型:
type PotentialType1 = { id: string name: string someProperty: string } type PotentialType2 = { id: string name: string someOtherProperty: string }
目前遇到的问题是:按上述写法实现时,myFunction中访问的所有属性都会抛出如下错误:
Property X does not exist on type 'MyGenericType'.
即id、name、someProperty等属性都提示不存在于MyGenericType类型上。已知泛型对string、number等原始类型可以完美适配,需要知道针对这类复杂对象类型的正确实现方式。
报错核心原因是未给泛型添加上界约束。TypeScript中未加约束的泛型默认可以匹配任意类型,既可以是对象,也可以是数字、字符串、null等原始值/空值,TS无法确认泛型上一定存在访问的id、name等属性,就会抛出属性不存在的错误。
可以根据实际场景选择以下两种实现方式:
方式1:给泛型添加约束(保留泛型推导能力,扩展性更好)
先抽取两种类型的公共必选属性,将差异属性设为可选,作为泛型的约束规则,限制传入的泛型必须符合该结构,TS就能正确识别属性:
// 定义泛型的约束边界 type BaseConstraint = { id: string name: string someProperty?: string someOtherProperty?: string } // 通过 extends 给泛型绑定约束,无需手动给map回调的obj标注类型,TS可自动推导 const myFunction = <T extends BaseConstraint>(arrayOfObjects: T[]) => { return arrayOfObjects.map((obj) => ({ id: obj.id, name: obj.name, someProperty: obj.someProperty || obj.someOtherProperty, })) }
这种写法的优势是保留了泛型的类型推导能力:传入PotentialType1[]时,TS能自动识别数组元素存在someProperty属性;传入PotentialType2[]时,能自动识别数组元素存在someOtherProperty属性,后续如果要新增第三种同结构的入参类型,也无需修改函数逻辑。
方式2:直接使用联合类型作为参数类型(固定入参场景更简洁)
如果确定函数只会接收上述两种固定类型的数组,可以不使用泛型,直接将参数声明为两种类型的联合数组:
const myFunction = (arrayOfObjects: (PotentialType1 | PotentialType2)[]) => { return arrayOfObjects.map((obj) => ({ id: obj.id, name: obj.name, // 用in操作符做类型守卫,比直接用||更严谨,避免TS报属性不存在的错误 someProperty: 'someProperty' in obj ? obj.someProperty : obj.someOtherProperty, })) }
内容的提问来源于stack exchange,提问作者theJuls

