我正在将更好的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相关问答推荐

如何在ts中使用函数重载推断参数类型

如何从TypScript中的接口中正确获取特定键类型的所有属性?

为什么在TypScript中写入typeof someArray[number]有效?

界面中这种类型的界面界面中的多态性

TypeScript类型不匹配:在返回类型中处理已经声明的Promise Wrapper

Angular 行为:属性类型从SignalFunction更改为布尔

React typescribe Jest调用函数

如何在TypeScrip中使用嵌套对象中的类型映射?

类型脚本中没有接口的中间静态类

我可以以这样一种方式键入对象,只允许它的键作为它的值吗?

类型TTextKey不能用于索引类型 ;TOption

使用2个派生类作为WeakMap的键

如何编写在返回函数上分配了属性的类型安全泛型闭包

TYPE FOR ARRAY,其中每个泛型元素是单独的键类型对,然后在该数组上循环

如何键入对象转换器

有没有一种方法可以防止我的组件包在其中安装样式组件'; node 模块中的s目录

为什么TS条件返回要求不明确的强制转换而不是精确的强制转换?

在 next.js 13.4 中为react-email-editor使用forwardRef和next/dynamic的正确方法

Mongodb TypeError:类继承 events_1.EventEmitter 不是对象或 null

可选字段和联合之间有什么区别吗?