typescript 只有< T>在缺少类型时,Mutable才起作用

icomxhvb  于 2022-12-24  发布在  TypeScript
关注(0)|答案(1)|浏览(113)

我创建了一个Mutable类型实用程序,目的是将不可变的结构转换为可变的结构。这包括readonly xxx[]类型以及Readonly<T>操作符。在我的本地环境中,它不工作,所以我将它复制到TS Playground,令我震惊和恐惧的是,它工作得很好。然后我注意到我的类型实用程序依赖于一个不存在的类型实用程序。我“我原以为我会以一个 * 任何 * 类型或同样丑陋的东西结束,但没有...它的工作。
这是操场的状况:破碎时工作
突变的代码为:

export type Mutable<T> = {
  -readonly [K in keyof T]: IsObject<T[K]> extends true 
    ? Mutable<T[K]> 
    : T[K] extends Readonly<any>
      ? T[K] extends Readonly<infer R>
        ? R
        : never
      : T[K];
};

这段代码依赖于我创建的一个IsObject实用程序,但是忘记带进上面的游戏中了,这个实用程序看起来像这样:

type IsObject<T> = Mutable<T> extends Record<string, any>
  ? T extends <A extends any[]>(...args: A) => any
    ? false  // when a function with props is found, categorize as a function not object
    : true
  : false;

以前缺少 IsObject 的新Playground现在包括:Playground
可悲的是,前两项测试不再通过:

传入这两个前测试的类型相似:

传递给Mutate<T>的这些函数产生相同的结果,并且进行了一些有用的转换,但是虽然bazreadonly 数组转换为可变数组,但属性bar仍保留在Readonly<"yes" | "no">

有人能帮我理解如何删除这些Readonly<T> prop 吗?有人能解释一下当我使用一个不存在的类型实用程序时是如何工作的吗?
正在添加评论...
使用readonly修饰符的限制示例:

dnph8jn4

dnph8jn41#

从@TobaisS那里得到了一个非常有用的推动,我现在已经创建了这个类型,它递归地、干净地解决了readonly修饰符示例的问题空间,并接受了Readonly<T>修饰符目前不是绝对必要的(尽管它仍然在我的“最好拥有”列表中)。

/**
 * Makes a readonly structure mutable
 */
export type Mutable<T> = ExpandRecursively<{
  -readonly [K in keyof T]: T[K] extends {} 
    ? Mutable<T[K]> 
    : T[K] extends readonly (infer R)[]
      ? R[]
      : T[K];
}>;

其中ExpandResursively<T>工具用于展开递归并给予一个干净的类型。

export type ExpandRecursively<T> = T extends object
  ? T extends (...args: any[]) => any
    // Functions should be treated like any other non-object value
    // but will/can identify as an object in JS
    ? T
    : { [K in keyof T]: ExpandRecursively<T[K]> }
  : T;

使用这两个实用程序,您可以获取如下递归输入:

type T = {
  foo: number;
  bar: readonly string[];
  readonly baz: string;
  nested: {
    one: number;
    readonly two: number;
    readonly literal: "foo" | "bar";
  };
};

并使用Mutable<T>转换为:

{
  foo: number;
  bar: string[];
  baz: string;
  nested: {
    one: number;
    two: number;
    literal: "foo" | "bar";
  };
}

相关问题