如何在 Typescript 中使用泛型动态设置特定类型的值

How to dynamically set a value of a specific type using generics in Typescript

假设我有一个通用实用程序 class,它应该与 class 接口,并且有一个方法 setPropertiesToTrue 接受 class 属性列表,并设置这些属性实例里面的 class 为 true.

class SomeObject{
  x: boolean 
  y: boolean
  z: string

  constructor(x: boolean,y: boolean,z: string){
    this.x = x,
    this.y = y,
    this.z = z
  } 
}

class ProperySetter<TSomeObject>{
  objectContext: TSomeObject

  constructor(objectContext: TSomeObject){
    this.objectContext = objectContext
  }

  setPropertiesToTrue(properties: Array<keyof TSomeObject>){
    properties.forEach(property => {
      this.objectContext[property] = true
    })
  }
}


const someObject = new SomeObject(false, false, "hello")

const propertySetter = new ProperySetter(someObject);

propertySetter.setPropertiesToTrue(["x", "y"])

console.log(someObject.x) // changed from false to true
console.log(someObject.y) // changed from false to true
console.log(someObject.z) // unchanged


Here 是一个 TS Playground 示例,或多或少是我正在尝试做的事情。

在上面的例子中,(在 playground 的第 22 行),打字稿似乎不知道作为属性传递的键对应于通用 TSomeObject 中的布尔值。所以我得到了错误:

Type 'boolean' is not assignable to type 'TSomeObject[keyof TSomeObject]'.

这是有道理的,因为 keyof TSomeObject 对应于 TSomeObject 的所有键,而不仅仅是与布尔键对应的键。

有办法解决这个问题吗?

您可以加​​强输入数组的约束,使其仅包含 boolean 值的键。然而,TS 不够聪明,无法知道数组中的键是有效的。因此,您将只需要强制转换就可以使代码正常工作。以下是对键类型具有更好约束的工作代码示例:

class SomeObject{
  x: boolean 
  y: boolean
  z: string

  constructor(x: boolean, y: boolean, z: string){
    this.x = x,
    this.y = y,
    this.z = z
  } 
}

class ProperySetter<TSomeObject>{
  objectContext: TSomeObject

  constructor(objectContext: TSomeObject){
    this.objectContext = objectContext
  }

  setPropertiesToTrue(properties: Array<keyof { [K in keyof TSomeObject as TSomeObject[K] extends boolean ? K : never]: 0 }>){
    properties.forEach(property => {
      (this.objectContext as any)[property] = true
    })
  }
}


const someObj = new SomeObject(true, true, "hey");
const setter = new ProperySetter(someObj);
setter.setPropertiesToTrue(["x", "y"])

// @ts-expect-error not a boolean property
setter.setPropertiesToTrue(["z"]);

TypeScript Playground Link