假设我们有一个接口和一个enum
enum Destination = {
Home = 'home',
Cafe = 'cafe'
}
interface Payload {
apiVersion: string;
destination: Destination;
email?: string;
}
我想创建一个模板文字类型,它将接口键作为开始和结束标记,并将键值作为标记值。
const condensedPayload: CondensedPayload = '<payload><apiVersion>2.1</apiVersion><destination>home</destination><email>asd@asd.com</email></payload>'
我已经尝试创建这样的CondensedPayload
type CondensedPayload = `<payload><${keyof Payload}>${Payload[keyof Payload]}</${keyof Payload}></payload>`
结果:
- 关闭标签名称不一样的开始标记名称,反之亦然,没有得到一个错误
const payload: CondensedPayload = '<payload><apiVersion>2.0</destination></payload>' // no error
- 不插入所需的键不会得到错误
const payload: CondensedPayload = '<payload><email>asd@asd.com</email></payload>' // no error
- 插入只有一个关键要求不得到一个错误
const payload: CondensedPayload = '<payload><apiVersion>2.0</apiVersion><email>asd@asd.com</email></payload>' // no error
- 在
<destination>some string</destination>
上插入Destination
外的字符串没有得到错误
是否有一种方法来匹配打开,关闭和值,比如迭代keyof Payload
和值类型?
可能吗?是的。你应该这么做吗?也许不是…
下面的解决方案将生成对给定Payload
类型有效的所有可能的字符串文字类型组合。
type Expand<T> = T extends infer U ? { [K in keyof U]: U[K] } : never
type OptionalProps<T extends object> = Exclude<{
[K in keyof T]: T extends Record<K, T[K]>
? never
: K
}[keyof T], undefined>
type AllPermutations<T extends string, O extends string> = [T] extends [keyof T] ? [] : {
[K in T]:
K extends O
? [
...(AllPermutations<Exclude<T, K>, O> extends infer U extends string[]
? U
: never)
]
| [
K, ...(AllPermutations<Exclude<T, K>, O> extends infer U extends string[]
? U
: never)
]
: [
K, ...(AllPermutations<Exclude<T, K>, O> extends infer U extends string[]
? U
: never)
]
}[T]
type Join<T extends string[]> =
T extends [infer L extends string, ...infer R extends string[]]
? `${L}${Join<R>}`
: ""
type CreatePayloadString<T extends string[], P extends Record<string, any>> =
T extends [infer L extends string, ...infer R extends string[]]
? `<${L}>${P[L & keyof P]}</${L}>${CreatePayloadString<R, P>}`
: ""
type CondensedPayload = {
[K in AllPermutations<keyof Payload, OptionalProps<Payload>> as Join<K>]:
`<payload>${CreatePayloadString<K, Payload>}</payload>`
} extends infer O ? O[keyof O] : never
一些测试:
// Error
const payload1: CondensedPayload = '<payload><apiVersion>2.0</destination></payload>'
const payload2: CondensedPayload = '<payload><email>asd@asd.com</email></payload>'
const payload3: CondensedPayload = '<payload><apiVersion>2.0</apiVersion><email>asd@asd.com</email></payload>'
const payload4: CondensedPayload = '<payload><apiVersion>abc</apiVersion><destination>some string</destination></payload>'
// Valid
const payload4: CondensedPayload = '<payload><apiVersion>abc</apiVersion><destination>home</destination></payload>'
但是,严肃地说,使用XML解析器来处理这样的事情,甚至更好:不要使用XML。
游乐场