具有元组/联合支持的深度转换的文字脚本映射类型 [英] TypeScript mapped type that deep transforms with tuple/union support
本文介绍了具有元组/联合支持的深度转换的文字脚本映射类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我正在尝试创建实现递归类型转换的通用映射类型。
非常感谢@jcalz为https://stackoverflow.com/a/60437613/1401634提供优雅的解决方案。
(请注意,票证具有不同的作用域,不与此票证重复)
如下所示,当前映射类型不支持元组或联合类型。
是否有办法支持联合类型并使规范通过?
游乐场就绪👉Playground Link
/**
* Recursive type transformation. Support scalar, object, array, and tuple within original type.
* @example
* DeepReplace<Original, [From, To] | [Date, string] | ...>
*/
type DeepReplace<T, M extends [any, any]> = T extends M[0] ?
Replacement<M, T>
:
{
[P in keyof T]: T[P] extends M[0]
? Replacement<M, T[P]>
: T[P] extends object
? DeepReplace<T[P], M>
: T[P];
}
type Replacement<M extends [any, any], T> =
M extends any ? [T] extends [M[0]] ? M[1] : never : never;
// Tests
const obj = {
number: 1,
date: new Date(),
deep: { date: new Date() },
arrayDeep: [{ date: new Date() }],
array: [new Date()],
tuple: [new Date(), 2, true],
tupleWithObj: [{ date: new Date() }, 2, 'hi', { hello: 'world' }],
tupleWithTuple: [[1, false], [2, new Date()], [3, { date: new Date() }]]
}
type ArrayType<A extends unknown[]> = $ElementType<A, number>
const date = new Date()
const number = 2
const n = null
const nestedArray = [[[new Date()]]]
const scalarTest: DeepReplace<typeof date, [Date, string]> = 'string' // ✅
const constTest: DeepReplace<typeof number, [Date, string]> = 2 // ✅
const primitiveTest: DeepReplace<typeof n, [Date, string]> = null // ✅
const nestedArrayTest: DeepReplace<typeof nestedArray, [Date, string]> = [[['string']]] // ✅
let o: DeepReplace<typeof obj, [Date, string]>
const innocentTest: typeof o.number = 2 // ✅
const shallowTest: typeof o.date = 'string' // ✅
const deepTest: typeof o.deep.date = 'string' // ✅
const arrayTest: ArrayType<typeof o.array> = 'string' // ✅
const arrayObjTest: ArrayType<typeof o.arrayDeep>['date'] = 'string' // ✅
const tupleTest: typeof o.tuple = ['string'] // ❌ Type 'string' is not assignable to type 'number | boolean | Date'.
const tupleObjTest: typeof o.tupleWithObj = { date: 'string' } // ❌ Object literal may only specify known properties, and 'date' does not exist in type '(string | number | { date: Date; soHard?: undefined; } | { soHard: string; date?: undefined; })[]'
const tupleTupleTest: typeof o.tupleWithTuple = [[1, false], [2, 'string'], [3, { date: 'string' }]] // ❌ Type 'string' is not assignable to type 'number | boolean | Date | { date: Date; }'; Type 'string' is not assignable to type 'Date'.
推荐答案
有两个部分(需要两件事才能使它们工作)
联合类型
元组类型
您需要使用infer
keyword
汇总:
/**
* Recursive type transformation. Support scalar, object, array, and tuple as original type.
* @example
* DeepReplace<Original, [From, To] | [Date, string] | ...>
*/
type DeepReplace<T, M extends [any, any]> = T extends M[0] ?
Replacement<M, T>
:
{
[P in keyof T]: T[P] extends M[0]
? Replacement<M, T[P]>
: T[P] extends (infer R)[] // Is this a Tuple or array
? DeepReplace<R, M>[] // Replace the type of the tuple/array
: T[P] extends object
? DeepReplace<T[P], M>
: Extract<T[P], M[0]> extends M[0] // Is this a union with the searched for type?
? UnionReplacement<M, T[P]> // Replace the union
: T[P];
}
type Replacement<M extends [any, any], T> =
M extends any ? [T] extends [M[0]] ? M[1] : never : never;
type UnionReplacement<M extends [any, any], T> =
DeepReplace<Extract<T, object>, M> // Replace all object types of the union
| Exclude<T, M[0] | object> // Get all types that are not objects (handled above) or M[0] (handled below)
| M[1]; // Direct Replacement of M[0]
另外,对于任何阅读本文以转换对象的人,您仍然需要真正转换它们,这只是更改了TypeScrip的类型,并不能保证您将获得正确的对象,您仍然需要进行JS样式的转换
这篇关于具有元组/联合支持的深度转换的文字脚本映射类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文