打字稿:有递归键吗?

丹尼尔·卡普兰

有没有办法让这样的代码编译并保持类型安全?

type ComplexObject = {
  primitive1: boolean;
  complex: {
    primitive2: string;
    primitive3: boolean;
  }
};

interface MyReference {
  myKey: keyof ComplexObject;
}

const works1: MyReference = {
  myKey: "primitive1"
}

const works2: MyReference = {
  myKey: "complex"
}

const iWantThisToCompile1: MyReference = {
  myKey: "complex.primitive2" // Error: Type '"complex.primitive2"' is not assignable to type '"primitive1" | "complex"'.
}

const iWantThisToCompile2: MyReference = {
  myKey: "complex['primitive3']" // Error: Type '"complex['primitive3']"' is not assignable to type '"primitive1" | "complex"'.
}

// const iDontWantThisToCompile1: MyReference = {
//  myKey: "primitive2"
// }

// const iDontWantThisToCompile2: MyReference = {
//  myKey: "primitive3"
// }

您可以在此处使用此代码

大卫·谢瑞

使用TypeScript 4.1中的新模板文字类型和递归类型,可以做到这一点

属性和索引访问类型

这里有几种定义此方法的方法,它们可以作用于单个级别。我建议第一种方法,因为它的公共API中没有其他未使用的类型参数。

export type RecursiveKeyOf<TObj extends object> = {
  [TKey in keyof TObj & (string | number)]:
    RecursiveKeyOfHandleValue<TObj[TKey], `${TKey}`>;
}[keyof TObj & (string | number)];

type RecursiveKeyOfInner<TObj extends object> = {
  [TKey in keyof TObj & (string | number)]:
    RecursiveKeyOfHandleValue<TObj[TKey], RecursiveKeyOfAccess<TKey>>;
}[keyof TObj & (string | number)];

type RecursiveKeyOfHandleValue<TValue, Text extends string> =
  TValue extends object
    ? Text | `${Text}${RecursiveKeyOfInner<TValue>}`
    : Text;

type RecursiveKeyOfAccess<TKey extends string | number> =
  | `['${TKey}']`
  | `.${TKey}`;
export type RecursiveKeyOf<TObj extends object, isFirstLevel extends boolean = true> = {
  [TKey in keyof TObj & (string | number)]:
    isFirstLevel extends true
      ? RecursiveKeyOfHandleValue<TObj[TKey], `${TKey}`>
      : RecursiveKeyOfHandleValue<TObj[TKey], RecursiveKeyOfAccess<TKey>>;
}[keyof TObj & (string | number)];

type RecursiveKeyOfHandleValue<TValue, Text extends string> =
  TValue extends object
    ? Text | `${Text}${RecursiveKeyOf<TValue, false>}`
    : Text;

type RecursiveKeyOfAccess<TKey extends string | number> =
  | `['${TKey}']`
  | `.${TKey}`;

仅属性访问类型

如果只需要属性访问,则要简单得多:

export type RecursiveKeyOf<TObj extends object> = {
  [TKey in keyof TObj & (string | number)]:
    TObj[TKey] extends object
      ? `${TKey}` | `${TKey}.${RecursiveKeyOf<TObj[TKey]>}`
      : `${TKey}`;
}[keyof TObj & (string | number)];

说明与细目

export type RecursiveKeyOf<TObj extends object> = (
  (
    // 1. Create an object type from `TObj`, where all the individual
    // properties are mapped to a string type if the value is not an object
    // or union of string types containing the current and descendant
    // possibilities when it's an object type.
    {
      // Does this for every property in `TObj` that is a string or number
      [TPropName in keyof TObj & (string | number)]:
        HandleProperty<TObj[TPropName], TPropName>;
    }
  )[
    keyof TObj & (string | number) // for every string or number property name
  ] // 2. Now flatten the object's property types to a final union type
);

type HandleProperty<TValue, TPropName extends string | number> =
  // If the value of the property is an object type...
  TValue extends object
    // Then...
      // 1. Return the current property name as a string
    ? `${TPropName}`
      // 2. And return the property name concatenated with a `.` and
      //    all the return values of `RecrusiveKeyOf<TValue>`
      | `${TPropName}.${RecursiveKeyOf<TValue>}`
    // Else, only return the current property name as a string
    : `${TPropName}`;

例如:

// this type
{
  prop: { a: string; b: number; };
  other: string;
}

// goes to
{
  prop: "prop" | "prop.a" | "prop.b";
  other: "other";
}

// goes to
"prop" | "prop.a" | "prop.b" | "other"

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Elixir有打字稿吗?

来自分类Dev

打字稿:具有单个键的对象

来自分类Dev

打字稿:您可以递归键入对象吗

来自分类Dev

带有全能道具的可能递归对象的打字稿接口

来自分类Dev

打字稿:强制执行现有的对象键

来自分类Dev

打字稿:从具有泛型的对象获取键映射

来自分类Dev

具有未知属性键的打字稿接口定义

来自分类Dev

打字稿:接口键枚举

来自分类Dev

打字稿中的相交键

来自分类Dev

打字稿足够健壮吗?

来自分类Dev

如何辨别打字稿“具有除其他类型的键以外的任何键的对象”

来自分类Dev

打字稿递归类型交集

来自分类Dev

打字稿:可以使用自己的键对类型进行参数化吗?

来自分类Dev

已知键和未知键的打字稿打字

来自分类Dev

是否有更好的方法在打字稿中编写此递归方法

来自分类Dev

在打字稿中递归转换对象树的所有叶子

来自分类Dev

当键不存在时,有没有办法使打字稿抛出错误?

来自分类Dev

打字稿打字:具有属性的函数

来自分类Dev

打字稿RequireJs快捷键+导航

来自分类Dev

打字稿遍历键入的对象键

来自分类Dev

打字稿:删除多个键循环数组

来自分类Dev

打字稿模板文字作为接口键

来自分类Dev

打字稿RequireJs快捷键+导航

来自分类Dev

打字稿:枚举键作为函数的参数

来自分类Dev

在打字稿中获取类的键

来自分类Dev

取决于参数,有时在打字稿对象上存在的键

来自分类Dev

如何在打字稿中声明带有部分特定键的“记录”类型?

来自分类Dev

描述打字稿功能,该功能返回带有数组键的修改对象

来自分类Dev

从打字稿中的对象列表中查找键的唯一值的有效方式

Related 相关文章

热门标签

归档