将 as const 数组展开为其可变的非元组对应项

moc*_*cha 3 typescript

我有一个数组as const

// just a simple mock class
class Child { _ = "" }

const child = new Child();

const schema = [[child], child] as const; // readonly [readonly [Child], Child];
Run Code Online (Sandbox Code Playgroud)

该数组表示类型的联合,因此在这里,它将表示一个数组Child或另一个数组Child(更深一层)。本质上,我想将 的类型转换schema(Child | Child[])[].

type T = UnwrapAsConstArray<readonly [readonly [Child], Child]> // => (child | Child[])[]
Run Code Online (Sandbox Code Playgroud)

我正在努力寻找使这种转变成为可能的逻辑。是我可怜的尝试,它的效果并不像你在底部看到的那么好。

一个可以尝试制定解决方案的游乐场,其中包含一些测试用例和预期行为。

请记住,我希望解决方案是递归的并且适用于任意数量的嵌套。

类似的问题:从不带 "as const" 的数组创建类型元组。我需要“相反”,我知道这是可能的。

jca*_*alz 5

我的方法如下:

type UnwrapAsConstArray<T> = 
  T extends readonly any[] ? UnwrapAsConstArray<T[number]>[] :
  { -readonly [K in keyof T]: UnwrapAsConstArray<T[K]> }
Run Code Online (Sandbox Code Playgroud)

这与表单DeepMutable<T>的普通类型非常相似

type DeepMutable<T> = { -readonly [K in keyof T]: DeepMutable<T[K]> }
Run Code Online (Sandbox Code Playgroud)

这将使基元保持不变,并递归地readonly从任何对象类型中剥离,将数组保留为数组,将元组保留为元组。唯一的区别是检查readonly any[],我们显式地获取其元素类型的并集T[number],并UnwrapAsConstArray对其执行生成一个常规数组。

这通过了示例中的所有测试:

type UnwrappedSchema = UnwrapAsConstArray<typeof schema>;
//   ^? type UnwrappedSchema = ({ _: string; } | { _: string; }[])[]
// not written as `(Child | Child[])[]`, but structrually identical

type Test01 = Assert<Equals<
  UnwrappedSchema, (Child | Child[])[]>>; // okay

type Test02 = Assert<Equals<UnwrapAsConstArray<
  readonly [readonly [1], 2]>, (2 | 1[])[] // okay
>>;

type Test03 = Assert<Equals<UnwrapAsConstArray<
  readonly [1, readonly [2, 3], readonly [readonly [4, 5, 6]]]>, 
  (1 | (2 | 3)[] | (4 | 5 | 6)[][])[] // okay
>>;

type Test04 = Assert<Equals<UnwrapAsConstArray<
  readonly []>, never[] // okay
>>;

type Test05 = Assert<Equals<UnwrapAsConstArray<
  readonly [1]>, 1[] // okay
>>;
Run Code Online (Sandbox Code Playgroud)

请注意,这将递归地从Child您放入其中的任何对象类型中去除只读性,并且它不知道何时停止。任何不能在类型映射中生存的东西都会很奇怪(函数类型将变得只是{})。如果您需要针对这些情况进行调整,则必须添加逻辑来执行此操作。

Playground 代码链接