就像在问题中一样,有没有办法创建递归的Readonly <T>实用程序类型,它:

  • 判断类型T是基元类型还是扩展类型{}
  • 如果类型T递归地扩展{},则将RecursiveReadonly <T>应用于其属性

伪代码:

type RecursiveReadonly <T> = {
  readonly [P in keyof T]: 
    T === object ? 
      RecursiveReadonly <T[P]> :
      T[P]
};

PS.正常情况下,这应该是可行的

type RecursiveReadonly <T> = {
  readonly [P in keyof T]: RecursiveReadonly <T[P]>
};

然而...type FOO = RecursiveReadonly <{ foo: boolean }>;创建类型:type FOO = { readonly foo: RecursiveReadonly <boolean>,在某些模糊的情况下(我现在无法提供),tsc抛出错误RecursiveReadonly <boolean> is not assignable to boolean.我认为这只适用于基元类型,因此本例中使用的boolean类型是所有基元类型的占位符.

推荐答案

您要查找的语法是T extends U ? X : Y形式的conditional type(因此是extends,而不是===).您可以进行该更改,您的代码将正常工作,但当您判断RecursiveReadonly<{ foo: boolean }>时,它仍将显示{ readonly foo: RecursiveReadonly<boolean> }.这实际上相当于{ readonly foo: boolean },即使在您最初的无条件版本中也是如此,但我会相信您的话,您遇到了一些编译器对它犹豫不决的情况.

要获得您想要的类型,我会将条件类型移到实用程序类型的顶层,如下所示:

type RecursiveReadonly<T> = T extends object ? {
  readonly [P in keyof T]: RecursiveReadonly<T[P]>
} : T

这会给类型判断器一个提示,提示您希望看到RecursiveReadonly<T>显示为"展开"类型(请参见How can I see the full expanded contract of a Typescript type?).现在,您将看到预期的类型:

type FOO = RecursiveReadonly<{ foo: boolean }>;
// type FOO = { readonly foo: boolean; }

Playground link to code

Typescript相关问答推荐

类型脚本如何将嵌套的通用类型展开为父通用类型

根据另一个属性的值来推断属性的类型

无法正确推断嵌套泛型?

类型脚本基于数组作为泛型参数展开类型

TypeScrip省略了类型参数,仅当传递另一个前面的类型参数时才采用默认类型

一个打字类型可以实现一个接口吗?

为什么Typescript不能推断类型?

Vue3 Uncaught SyntaxError:每当我重新加载页面时,浏览器控制台中会出现无效或意外的令牌

为什么在leetcode问题的测试用例中,即使我确实得到了比预期更好的解决方案,这段代码也失败了?

自定义 Select 组件的类型问题:使用带逗号的泛型类型<;T与不带逗号的<;T&>;时出错

如何键入派生类的实例方法,以便它调用另一个实例方法?

有没有办法传递泛型类型数组,以便推断每个元素的泛型类型?

VUE 3类型';字符串';不可分配给类型';参考<;字符串&>

将类型脚本函数返回类型提取到VSCode中的接口

有没有一种更好的方法来存储内存中的数据,而不是在类型脚本中使用数组和基于索引的函数?

是否可以定义引用另一行中的类型参数的类型?

递归类型别名的Typescript 使用

组件使用forwardRef类型脚本时传递props

两个接口的TypeScript并集,其中一个是可选的

从函数参数推断函数返回类型