在TypeScript接口密钥迭代中使用条件类型

时间:2018-06-25 14:40:34

标签: typescript

嗨,他有2个接口和一个类型

interface A { k1: string k2: string }

interface B { k3: string k4: A }

type Error = { key:string, value:string}

我已经开发了一个声明,该声明使用给定接口的所有键和类型= Error []

创建一个接口

type AutoErrors<P> = { [K in keyof P]-?: Error[] }

这很好,因为AutoErrors<A>给出了{ k1: Error[] k2: Error[] }

但是现在我希望它是递归的,即,如果参数类型键不是字符串类型,我希望它的类型为AutoErrors<T>

例如AutoErrors<B>应该是{ k3: Error[], k4: AutoErrors<A> }

我尝试过

type AutoErrors<X> = { [K in keyof X]-?: X[K] extends string ? Error[] : AutoErrors<X[K]> } 但却无法正常工作,好像测试X [K]扩展字符串的解释不正确

2 个答案:

答案 0 :(得分:1)

您可以使用类似于DeepReadOnly的方法

export type primitive = string | number | boolean | undefined | null
export type DeepError<T> = 
    T extends (infer U)[] ? Array<DeepErrorObject<U>> :
    T extends primitive ? Error[] : DeepErrorObject<T>;

export type DeepErrorObject<T> = {
readonly [P in keyof T]: DeepError<T[P]>
}
// sample usage
interface A {
    k1: string
    k2: string
} 
interface B {
    k3: string
    k4: A
    k4Arr: B[]
}
type Error = { 
    key:string, 
    message:string
}

type ErrorA = DeepError<B>
let d: ErrorA;
d.k4Arr[0].k3; // Error[]

答案 1 :(得分:1)

您的问题works in the Playground中发布的代码,因此我怀疑还有其他情况。事实证明,问题与可选属性有关,例如

type UhOh = AutoErrors<{ a?: string }>; // becomes {a: string}, not good

在这种情况下,原始的AutoErrors<>定义不起作用,因为string | undefined不扩展string。如您所知,解决此问题的方法是检查string | undefined而不只是string

type AutoErrors<X> = {
  [K in keyof X]-?: X[K] extends string | undefined ?
  Error[] : AutoErrors<X[K]>
}

有效:

type Okay = AutoErrors<{ a?: string }>; // becomes {a: Error[]}, as expected

很高兴您发现发生了什么事。