/**/

我正在将更好的Typescript 应用到一个项目中.有一个复杂的对象来self 的后端,它是一个具有数十个属性的类,其中大多数是它们自己的类.我们有一个带有通用更改处理程序的表单,该处理程序当前有几个any来使其工作,但我想用泛型来修复它.

这是我的应用程序中模式的简化版本,但我能够重现我看到的相同错误:

class PhoneNumber {
  prefix: number;
  exchange: number;
  line: number;
}

class Address {
  address1: string;
  address2: string;
  city: string;
}

class User {
  phone: PhoneNumber;
  address: Address;
}

const user = new User();

function setValue<A extends keyof User, T extends User[A], K extends keyof T, V extends T[K]>(
  value: V,
  fieldType: new () => T,
  fieldName: A,
  fieldAttribute: K
) {

  if (!user[fieldName]) {
    user[fieldName] = new fieldType();
  }

  const field = user[fieldName];

  // error here.
  field[fieldAttribute] = value;
}

setValue(408, PhoneNumber, 'phone', 'prefix');

我try 了几种方法,这是我能找到的最接近的解决方案.我能够在我的IDE中判断末尾的函数调用,看起来泛型填充了一些合理的东西:function setValue<"phone", PhoneNumber, "prefix", number>(),但是在编译过程中我得到了一个错误:

TS2536:类型‘K’不能用于索引类型‘USER[A]’.

我是不是走错了路?如果我不能弄清楚这一点,我最终将把不同的类型划分到单独的处理程序中.

还有一种情况是,一些字段是对象的数组,这给整个事情增加了另一条皱纹.

推荐答案

使用当前参数类型setValue,可以将child类B提供为fieldType(其中extends user[fieldName]为A类),并以B的fieldAttribute为目标,这可能不存在于A中:

class PhoneNumber2 extends PhoneNumber {
  extension: number;
}

// Complies with setValue types,
// but if `user` already has a basic PhoneNumber instance,
// the code within setValue fails
setValue(408, PhoneNumber2, "phone", "extension");

// within setValue:
user["phone"]["extension"] = 408;
// ...but user["phone"] may be a PhoneNumber,
// not necessarily a PhoneNumber2!

在您的例子中,不是K extends keyof T(T是fieldType的返回类型,可能是子类),而是只需使用K extends keyof User[A],以确保fieldAttribute指向基类:

function setValue2<A extends keyof User, T extends User[A], K extends keyof User[A], V extends T[K]>(
  value: V,
  fieldType: new () => T,
  fieldName: A,
  fieldAttribute: K
) {

  if (!user[fieldName]) {
    user[fieldName] = new fieldType();
  }

  const field = user[fieldName];

  field[fieldAttribute] = value; // Now okay
}

setValue2(408, PhoneNumber, 'phone', 'prefix');
setValue2(408, PhoneNumber2, 'phone', 'prefix');
setValue2(408, PhoneNumber2, 'phone', 'extension'); // Error: Argument of type '"extension"' is not assignable to parameter of type 'keyof PhoneNumber'.

您甚至可以go 掉泛型类型T:

function setValue3<A extends keyof User, K extends keyof User[A], V extends User[A][K]>(
    value: V,
    fieldType: new () => User[A],
    fieldName: A,
    fieldAttribute: K
) {}

Playground Link

Typescript相关问答推荐

我应该为这个 Typescript 组件使用什么props 和界面?

永远不会推断接口的值

扩展文字类型的通用参数与文字类型本身不同吗?

如何创建使用有关对象的信息来推断新类型的类型助手

React Router Custom Props 数组不加载,但自定义字符串加载

Typescript 中的内部类类型

Typescript :从具有多个相同类型的可区分联合中提取

Typescript 泛型采用类型的对象并返回类型方法返回类型

类型 'string[]' 不可分配给类型 '("toString" | "valueOf")[]'

从对象推断类型中的类型

Typescript - 从枚举访问值

Typescript 中的通配符返回类型,其中返回实现接口的对象

通过 onClick(TypeScript)单击功能组件时设置react 状态

在 TypeScript 中使用 Omit 作为泛型类型的问题

如何在Typescript 中使用加法赋值运算符?

传播到文字类型数组中

如何在 Typescript React 中迭代组件的子项?

Angular Material 组件不起作用:'mat-option' 不是已知元素

类型错误:JSX 元素类型 '{} |空 | undefined' 不是 JSX 元素的构造函数

错误 TS2339:Property 'split' does not exist on type 'string | string[]'. Property 'split' does not exist on type 'string[]'