结合使用分布式条件类型和泛型方法时出现的问题 [英] Issue when using distributive conditional types combined with generic method

查看:36
本文介绍了结合使用分布式条件类型和泛型方法时出现的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试创建一个泛型函数,该函数接收AND对象T并接收该对象T的字符串属性名。

我以https://www.typescriptlang.org/docs/handbook/advanced-types.html为例(部分:分布式条件类型)

我已经提出了一个不使用泛型的解决方案,但是当我将显式类型更改为泛型类型时,TypeScript将无法编译。

这是非通用版本:

export type TypedPropertyNames<T, P> = { [K in keyof T]: T[K] extends P ? K : never }[keyof T];
export type StringPropertyNames<T> = TypedPropertyNames<T, string>;

interface Test {
  test: string;
}
 
function non_generic(form: Test, field: StringPropertyNames<Test>): string {
  return form[field];
}

这行得通。

现在,当我将Test接口更改为泛型参数时,它将不再编译。

export type TypedPropertyNames<T, P> = { [K in keyof T]: T[K] extends P ? K : never }[keyof T];
export type StringPropertyNames<T> = TypedPropertyNames<T, string>;

function generic<T>(form: T, field: StringPropertyNames<T>): string {
  return form[field]; // This won't compile
}

这是预期行为吗?或者这是一个打字错误? 有没有人能给我指一下让通用版本正常工作的方向(没有任何破解)

更新1:

编译错误:

Type 'T[{ [K in keyof T]: T[K] extends string ? K : never; }[keyof T]]' is not assignable to type 'string'.

Playground link

推荐答案

编译器通常无法确定未解析条件类型的可赋值(即,由于T extends U ? V : W中的TU中的至少一个尚未完全指定而无法急于评估的条件类型)。

这更多的是一个设计限制,而不是bug(请参阅Microsoft/TypeScript#30728);编译器将不会像人类那样聪明(请注意:当机器起义发生时请回到这里并编辑此内容),因此我们不应期望它只会"注意"T[TypedPropertyName<T,P>] extends P应该始终为真。我们可以编写特定的启发式算法来检测这种情况并执行所需的缩减,但它必须能够非常快速地运行,以便在其无用的99%的时间内不会降低编译时间。

有没有人能给我指一下让通用版本正常工作的方向(没有任何破解)

这真的取决于您认为什么是黑客攻击。绝对最简单的做法是使用type assertion,它显式用于您知道某些内容是类型安全的,但编译器无法找出它的时候:

function generic<T>(form: T, field: StringPropertyNames<T>): string {
  return form[field] as any as string;  // I'm smarter than the compiler 🤓
}

或者您可以尝试引导编译器完成必要的步骤,以了解您正在做的操作是安全的。具体地说,编译器不理解Record<K, V>[K]可赋值给V(其中Record<K, V>the standard library中定义为其键在K中且其值在V中的映射类型)。因此,您可以像这样约束类型T

function generic<T extends Record<StringPropertyNames<T>, string>>(
  form: T,
  field: StringPropertyNames<T>
): string {
  return form[field]; // okay
}

现在编译器很高兴了。约束T extends Record<StringPropertyNames<T>, string>实际上根本不是约束,因为任何对象类型都会符合它(例如,{a: string, b: number}扩展Record<'a', string>)。因此,您应该能够在使用原始定义的任何地方使用它(无论如何对于具体类型T):

interface Foo {
  a: string;
  b: number;
  c: boolean;
  d: "d";
}
declare const foo: Foo;
generic(foo, "a"); // okay
generic(foo, "d"); // okay

那些是黑客吗?🤷‍♂️好的,希望能帮上忙。祝你好运!

这篇关于结合使用分布式条件类型和泛型方法时出现的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆