Javascript 通过动态查找关键字修改对象值TypeScript

Javascript 通过动态查找关键字修改对象值TypeScript,javascript,reactjs,typescript,Javascript,Reactjs,Typescript,我正在尝试编写一个函数,该函数返回另一个函数,该函数允许我修改TypeScript中对象的键。这是用于React减速器内 如果我有一个带有keysdogs、dogIds的状态对象,以及另一个带有keyscats和catIds的状态对象,我想编写一个patchGroup(group:string),这样,如果我传入cat或dog,它将返回一个函数,允许我修改这些键 我不熟悉打字脚本,所以我尝试用字符串索引。但是,TypeScript会出错,因为我无法使用字符串索引我的其他类型 显式声明/使用类型的

我正在尝试编写一个函数,该函数返回另一个函数,该函数允许我修改TypeScript中对象的键。这是用于React减速器内

如果我有一个带有keys
dogs
dogIds
的状态对象,以及另一个带有keys
cats
catIds
的状态对象,我想编写一个
patchGroup(group:string)
,这样,如果我传入
cat
dog
,它将返回一个函数,允许我修改这些键

我不熟悉打字脚本,所以我尝试用字符串索引。但是,TypeScript会出错,因为我无法使用字符串索引我的其他
类型

显式声明/使用类型的示例 例如,我为狗定义了以下状态:

interface DogsState {
  dogs: Array<Dogs>;
  dogIds: Array<string>;
  loading: boolean;
  // etc. 
} 
我现在想要一只猫:

接口CATSSState{
猫:阵列;
catIds:数组;
加载:布尔;
//等等。
} 
稍微修改
patchDog
是很简单的,我不想重复我的代码

当前尝试使用字符串(不工作)

常量patchGroup=(组:字符串)=>{
//与“patchDog”类似的逻辑`
常数补丁=(
州:DogsState | CatsState,
有效负载:DogResponse | DogCreateRequest | CatResponse | CatCreateRequest,
) => {
const groupIdx=state[`{$group}Id`].indexOf(有效负载[`${group}`][`${group}Id`]);
//字符串失败;需要以某种方式获取确切的密钥
}
返回补丁;
}
//======================状态对象=================
接口DogsState{
狗:阵列;
dogIds:数组;
加载:布尔;
//等等。
} 
接口CATS状态{
猫:阵列;
catIds:数组;
加载:布尔;
//等等。
} 
//======================请求=================
类型请求={
dogId:字符串;
};
类型DogCreateRequest={
狗:狗;
};
类型CatRequest={
catId:字符串;
};
类型CatCreateRequest={
猫:猫;
};
//======================回复=================
类型响应={
狗:狗;
最近更新:编号;
错误:字符串;
}
类型CatResponse={
猫:猫;
最近更新:编号;
错误:字符串;
}
//======================应用程序对象=================
接口狗{
dogId:字符串;
名称:字符串;
//……等等。
}
接口猫{
catId:字符串;
名称:字符串;
//等等。
}
到目前为止,我已经调查了:

  • (仍在阅读)

希望TypeScript在您的场景中验证类型安全性的主要问题是,TypeScript当前没有连接的方法。有关这方面的讨论,以及如果实施,将允许这一点的建议,请参阅。因此,虽然编译器理解
“cat”
是一种文本字符串类型,并且可以将其与
“dog”
区分开来,但它无法知道
“cat”+“Id”
将导致字符串文本类型
“catId”
。它只知道
“cat”+“Id”
属于
字符串类型

因此,当您使用
“Cat”+“Id”
索引到
Cat
时,它不知道该做什么。由于缺少字符串索引签名,它无法索引到具有通用
字符串的
Cat


所以这取决于你的目标是什么。如果您的目标只是抑制错误,而不担心编译器验证的安全性,那么您可以这样开始使用:

state[`{$group}Ids` as keyof typeof state]
const patchGroup = (group: string) => {
  const patch = (
    state: DogsState | CatsState,
    payload: DogResponse | DogCreateRequest | CatResponse | CatCreateRequest,
  ): DogsState | CatsState => {
    const groupIdx = (state[`{$group}Ids` as keyof typeof state] as any as Array<string>).
      indexOf((payload[`${group}` as keyof typeof payload] as Cat | Dog)[`${group}Id` as keyof (Cat | Dog)] as any as string);
    return {
      ...state,
      loading: false,
      [`{$group}s`]: [...state[`{$group}s` as keyof typeof state] as any as Array<Cat | Dog>, payload[group as keyof typeof payload]],
      [`{$group}Ids`]: [...state[`{$group}Ids` as keyof typeof state] as any as Array<string>, payload[group as keyof typeof payload][`{$group}Id`]]
    } as any as DogsState | CatsState;
  }
  return patch;
}
interface CatBundle {
  ks: "cats",
  id: "catId",
  ids: "catIds"
  obj: Cat,
  req: CatRequest,
  crq: CatCreateRequest,
  rsp: CatResponse,
  stt: CatsState
}
interface DogBundle {
  ks: "dogs",
  id: "dogId",
  ids: "dogIds"
  obj: Dog,
  req: DogRequest,
  crq: DogCreateRequest,
  rsp: DogResponse,
  stt: DogsState
}
interface Animals {
  dog: DogBundle,
  cat: CatBundle
}

// start implementation
const patchGroup = <K extends keyof Animals>(
  k: K) => (state: Animals[K]['stt'], payload: Animals[K]['rsp'] | Animals[K]['crq']
  ): Animals[K]['stt'] => {
    const id = k + "Id" as Animals[K]["id"];
    const ks = k + "s" as Animals[K]["ks"];
    const ids = k + "Ids" as Animals[K]["ids"];
但后来你遇到了一个新问题。。。您的内部功能似乎接受
状态
有效负载
,这是
Cat
版本的联合。这也不是类型安全的,因为它允许您调用
补丁组(“dog”)(someCatState,someCatPayload)

为了抑制这些合法的类型错误,您需要像这样开始到处断言:

state[`{$group}Ids` as keyof typeof state]
const patchGroup = (group: string) => {
  const patch = (
    state: DogsState | CatsState,
    payload: DogResponse | DogCreateRequest | CatResponse | CatCreateRequest,
  ): DogsState | CatsState => {
    const groupIdx = (state[`{$group}Ids` as keyof typeof state] as any as Array<string>).
      indexOf((payload[`${group}` as keyof typeof payload] as Cat | Dog)[`${group}Id` as keyof (Cat | Dog)] as any as string);
    return {
      ...state,
      loading: false,
      [`{$group}s`]: [...state[`{$group}s` as keyof typeof state] as any as Array<Cat | Dog>, payload[group as keyof typeof payload]],
      [`{$group}Ids`]: [...state[`{$group}Ids` as keyof typeof state] as any as Array<string>, payload[group as keyof typeof payload][`{$group}Id`]]
    } as any as DogsState | CatsState;
  }
  return patch;
}
interface CatBundle {
  ks: "cats",
  id: "catId",
  ids: "catIds"
  obj: Cat,
  req: CatRequest,
  crq: CatCreateRequest,
  rsp: CatResponse,
  stt: CatsState
}
interface DogBundle {
  ks: "dogs",
  id: "dogId",
  ids: "dogIds"
  obj: Dog,
  req: DogRequest,
  crq: DogCreateRequest,
  rsp: DogResponse,
  stt: DogsState
}
interface Animals {
  dog: DogBundle,
  cat: CatBundle
}

// start implementation
const patchGroup = <K extends keyof Animals>(
  k: K) => (state: Animals[K]['stt'], payload: Animals[K]['rsp'] | Animals[K]['crq']
  ): Animals[K]['stt'] => {
    const id = k + "Id" as Animals[K]["id"];
    const ks = k + "s" as Animals[K]["ks"];
    const ids = k + "Ids" as Animals[K]["ids"];
废话


从调用端恢复某种类型安全性的下一步是使
patchGroup()
成为一个只允许使用所有
Cat
或所有
Dog
输入调用自身的函数:

interface PatchGroup {
  cat: (state: CatsState, payload: CatResponse | CatCreateRequest) => CatsState,
  dog: (state: DogsState, payload: DogResponse | DogCreateRequest) => DogsState
}
const patchGroup = <K extends keyof PatchGroup>(group: K): PatchGroup[K] =>
  (state: any, payload: any) => {
    const groupIdx = state[`{$group}Ids`].indexOf((payload[`${group}`])[`${group}Id`]);
    return {
      ...state,
      loading: false,
      [`{$group}s`]: [...state[`{$group}s`], payload[group]],
      [`{$group}Ids`]: [...state[`{$group}Ids`], payload[group][`{$group}Id`]]
    };
  }

试图让编译器验证
patchGroup()
的实现是类型安全的,这可能是不值得的。这里的一个绊脚石是没有人支持我所说的。
CatXXX
接口之间的关系和
DogXXX
接口之间的关系很难表示为
CatXXX | DogXXX
联合之间的关系。即使我告诉编译器它需要知道的关于问题中使用的字符串文本之间关系的所有信息,比如:

state[`{$group}Ids` as keyof typeof state]
const patchGroup = (group: string) => {
  const patch = (
    state: DogsState | CatsState,
    payload: DogResponse | DogCreateRequest | CatResponse | CatCreateRequest,
  ): DogsState | CatsState => {
    const groupIdx = (state[`{$group}Ids` as keyof typeof state] as any as Array<string>).
      indexOf((payload[`${group}` as keyof typeof payload] as Cat | Dog)[`${group}Id` as keyof (Cat | Dog)] as any as string);
    return {
      ...state,
      loading: false,
      [`{$group}s`]: [...state[`{$group}s` as keyof typeof state] as any as Array<Cat | Dog>, payload[group as keyof typeof payload]],
      [`{$group}Ids`]: [...state[`{$group}Ids` as keyof typeof state] as any as Array<string>, payload[group as keyof typeof payload][`{$group}Id`]]
    } as any as DogsState | CatsState;
  }
  return patch;
}
interface CatBundle {
  ks: "cats",
  id: "catId",
  ids: "catIds"
  obj: Cat,
  req: CatRequest,
  crq: CatCreateRequest,
  rsp: CatResponse,
  stt: CatsState
}
interface DogBundle {
  ks: "dogs",
  id: "dogId",
  ids: "dogIds"
  obj: Dog,
  req: DogRequest,
  crq: DogCreateRequest,
  rsp: DogResponse,
  stt: DogsState
}
interface Animals {
  dog: DogBundle,
  cat: CatBundle
}

// start implementation
const patchGroup = <K extends keyof Animals>(
  k: K) => (state: Animals[K]['stt'], payload: Animals[K]['rsp'] | Animals[K]['crq']
  ): Animals[K]['stt'] => {
    const id = k + "Id" as Animals[K]["id"];
    const ks = k + "s" as Animals[K]["ks"];
    const ids = k + "Ids" as Animals[K]["ids"];
因此,我们可能已经超出了编译器可以帮助我们的范围。通过连接属性名并使用类型联合,我能想到的最好的方法是上面的调用安全实现不安全版本


从这里开始,下一步要做的就是停止试图强迫编译器理解这种字符串连接和计算属性代码,而是重构
Cat
Dog
接口以扩展单个
Animal
接口。不要给属性临时命名,如
catIds
dogIds
;相反,只需给他们相同的
animalIds
名称:

interface AnimalState<A extends Animal> {
  animals: Array<A>;
  animalIds: Array<string>;
  loading: boolean;
  // etc
}

interface DogsState {
  animals: Array<Dog>;
  animalIds: Array<string>;
  loading: boolean;
  // etc. 
}

interface CatsState {
  animals: Array<Cat>;
  animalIds: Array<string>;
  loading: boolean;
  // etc. 
}
编译器验证此实现是安全的,并且在调用站点也是安全的:

patch(catsState, catResponse); // okay
patch(dogsState, dogResponse); // okay
patch(catsState, dogResponse); // error! DogResponse not valid

可能有一些原因导致你不能进行重构,但它是如此的好,以至于我无论如何都会尝试,或者,如果失败的话,忘记将你原来重复的代码重构到一个单一的实现;复制很烦人,但至少编译器仍在帮助您

好吧,希望这会有帮助;祝你好运


考虑编辑问题中的代码,以构成中所述的。理想情况下,有人可以将代码放入一个独立的IDE中,以便自己演示这个问题。现在我不知道
patch(catsState, catResponse); // okay
patch(dogsState, dogResponse); // okay
patch(catsState, dogResponse); // error! DogResponse not valid