我试着写一个函数,它把对象A: { [key: string]: string | undefined }作为输入,输出一个新的对象B,使得A中的所有属性都存在于B中,每个string属性都转换为number,每个string | undefined属性都转换为number | undefined.

这说明了大体概念:

type In<T> = { [K in keyof T]: string | undefined };
type Out<T> = { [K in keyof T]: T[K] extends undefined ? number | undefined : number };

function transformObject<T extends In<T>>(input: T): Out<T> {
  const result = {} as Out<T>;
  for (const key in input) {
    const value = input[key];
    result[key] = value === undefined ? undefined : value.length;
  }
  return result;
}

const obj = { background: 'red', color: 'blue', border: undefined };
const obj2 = transformObject(obj);
const bgLength: number = obj2.background;
const borderLength: number | undefined = obj2.border;

问题是我不知道如何打字将赋值强制转换为result[key].没有as any类型的演员阵容.我得到以下错误:

Type 'number | undefined' is not assignable to type 'T[Extract<keyof T, string>] extends undefined ? number | undefined : number'.
  Type 'undefined' is not assignable to type 'T[Extract<keyof T, string>] extends undefined ? number | undefined : number'.ts(2322)

声明输入和输出对象之间的这种类型的关系的正确方式是什么,其中A中的所有属性都被声明为B中的属性,同时还将B中的可选属性保持为可选的?

推荐答案

里边

function transformObject<T extends In<T>>(input: T): Out<T> {
  const result = {} as Out<T>;
  for (const key in input) {
    const value = input[key];
    result[key] = value === undefined ? undefined : value.length; // error
  }
  return result;
}

result变量的类型为Out<T>,这是一种根据conditional type编写的类型,它依赖于generic类型参数T.当您try 为类型为keyof Tkey键的属性赋值时(好的,使用the Extract utility typeExtract<keyof T, string>,因为for-in循环跳过symbol个属性……不过,我们在这里只使用keyof T),然后编译器判断类型Out<T>[keyof T],发现它需要是泛型条件类型T[keyof T] extends undefined ? number | undefined : number.

但是众所周知,TypeScrip不能像这样对泛型条件类型进行太多分析.这类类型通常大部分没有求值,因此对类型判断器是不透明的.如果您try 为此类类型赋一个特定值,除非该值与您要赋值的类型相同,否则类型判断器不知道它是否为有效赋值.表达式value === undefined ? undefined : value.lengthunion type number | undefined中的一个.这与T[keyof T] extends undefined ? number | undefined : number不一样,所以编译器抱怨说.

您可能希望像t === v ? y : n这样的conditional expression会巧妙地映射到像T extends V ? Y : N这样的条件type上,但事实并非如此.判断value === undefined只影响变量value,而不影响其类型T[keyof T].泛型类型参数T完全不受该判断的影响.在其他更糟糕的情况下,control flow analysis目前不能很好地处理泛型类型.

所以你有点被困在这里了.GitHub中有各种功能请求,要求改进对此类情况的支持.这里最适用的可能是microsoft/TypeScript#33912,它显式地要求提供一种将控制流(如if/elseswitch/case?/:)与条件类型相关联的方法.GitHub问题讨论的是函数返回类型,但一般的问题是可赋值,因此它是适用的.即使只支持函数返回类型,您也可以使用它来编写帮助函数以使表达式正常工作.

在任何情况下,这种支持目前不是语言的一部分,您需要解决它.


最直接的解决方法是使用type assertion,并且只使用tell(它可以将(value === undefined ? undefined : value.length)视为类型Out<T>[keyof T]的编译器),然后继续前进:

function transformObject<T extends In<T>>(input: T): Out<T> {
  const result = {} as Out<T>;
  for (const key in input) {
    const value = input[key];
    result[key] = 
      (value === undefined ? undefined : value.length) as Out<T>[keyof T]; // okay
  }
  return result;
}

在这里,您正在将类型判断的责任从编译器手中移开,而编译器不能这样做.这意味着如果您在断言中犯了错误,编译器不能真正捕捉到它.所以你应该小心了.但至少在微软/TypeScrip#33912或类似的实现之前,并没有太多的事情要做.

Playground link to code

Typescript相关问答推荐

使用FormArray在Angular中添加排序

如何使用axios在前端显示错误体

如何为ViewContainerRef加载的Angular组件实现CanDeactivate保护?

如何修复正在处理类型但与函数一起使用时不处理的类型脚本类型

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

如何编写一个类型脚本函数,将一个对象映射(转换)为另一个对象并推断返回类型?

通过字符串索引访问对象';S的值时出现文字脚本错误

如何从扩展接口推断泛型?

在打印脚本中使用泛型扩展抽象类

将带点的对象键重新映射为具有保留值类型的嵌套对象

保护函数调用,以便深度嵌套的对象具有必须与同级属性函数cargument的类型匹配的键

为什么我在这个重载函数中需要`as any`?

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

为什么S struct 类型化(即鸭子类型化)需要非严格类型联合?

扩展对象的此内容(&Q;)

设置不允许相同类型的多个参数的线性规则

仅当定义了值时才按值筛选数组

确保财产存在

如何向我的自定义样式组件声明 custom.d.ts ?

使用受保护的路由和入门来响应路由