打字稿“类型不兼容”?

时间:2020-01-31 14:12:40

标签: typescript types

我正在构建Typesafe formbuilder,它应该产生一系列可执行函数,如下所示:FormBuilder.Entity(Pair(DefaultStudent, {}), q => q.Select('name', 'gender') .Children('courses', q => q.Select('code'))

一切正常,但是在q.select('code')部分出现类型错误,指出类型实体与其他类型不兼容。

这是用于生成表单的代码:

interface FormSelector<T, U> {
    entity: Pair<T, U> 
    Select: <K extends keyof T>(this: FormSelector<T, U>, ...properties: K[]) => FormSelector<Omit<T, K>, Pick<T, K> & U>
    Children: <K extends Filter<T, Array<any>>, P extends keyof ArrayType<T[K]>>(
        child: K,
        q: (_: FormSelector<ArrayType<T[K]>, U>) => FormSelector<Omit<ArrayType<T[K]>, P>, Pick<ArrayType<T[K]>, P>>
    ) => FormSelector<Omit<T, K>, U & {[key in K]: Array<Pick<ArrayType<T[K]>, P>>}>
}

interface FormBuilder {
    Entity: <T, U, K extends keyof T>(entity: Pair<T, U>, q: (_: FormSelector<T, U>) => FormSelector<Omit<T, K>, Pick<T, K> & U>) => Renderer<Pick<T, K> & U>
}

export let FormBuilder: FormBuilder = ({
    Entity: function <T, U, K extends keyof T>(entity: Pair<T, U>, q: (_: FormSelector<T, U>) => FormSelector<Omit<T, K>, Pick<T, K> & U>): Renderer<Pick<T, K> & U> {
        let x = q(FormSelector(entity)).entity.Second
        return { data: x }
    }
})

let FormSelector = <T, U>(e: Pair<T, U>): FormSelector<T, U> => ({
    entity: e,
    Select: function <K extends keyof T>(this: FormSelector<T, U>, ...properties: K[]): FormSelector<Omit<T, K>, Pick<T, K> & U> {
        return null!
    },
    Children: function <K extends Filter<T, Array<any>>, P extends keyof ArrayType<T[K]>>(
        child: K,
        q: (_: FormSelector<ArrayType<T[K]>, U>) => FormSelector<Omit<ArrayType<T[K]>, P>, Pick<ArrayType<T[K]>, P>>
    ): FormSelector<Omit<T, K>, U & {[key in K]: Array<Pick<ArrayType<T[K]>, P>>}> {
        return null!
    }
})


// This will be responsible for rendering the form, can be pas to a react component
interface Renderer<T> {
    data: T
}

以下是我使用的示例模型:

type Student = {
    id: number
    name: string
    paid: boolean
    gender: 'm' | 'v'
    courses: Course[]
}

type Course = {
    name: string
    code: string
    studypoint: number
}

const DefaultStudent: Student = {
    id: 1,
    gender: "m",
    name: '',
    paid: false, 
    courses: []
}

当我将FormSelector中的实体类型从Pair<T, U>更改为any时,它将进行编译,但是我失去了类型安全性。任何人都可以解释我所得到的错误或有什么想法导致此错误?

Playground

1 个答案:

答案 0 :(得分:0)

正如评论中所说,这个问题确实有很多事情发生,我修复了所有错误,然后看了出了什么问题。

FormBuilder接口有一个方法Entity(),女巫需要一个对象和一个lambda表达式,该表达式将a类型的FormSelector转换为b类型的FormSelector

interface FormBuilder {
Entity: <T1, T2, TResult>(entity: T1, q: (_: FormSelector<T1, Unit>) => FormSelector<T2, TResult>) => Renderer<TResult>
}

这里出了问题,它与Select()方法具有相同的签名,因此这意味着和功能链唯一可行的方法是Select()。导致q => q.Select(...).Children(...)不编译时出现类型错误。

此外,Children()中的FormSelector方法在lambda中不应将类型U作为第二类型参数。 qChildren的类型必须是一个将嵌套对象的FormSelector从T转换为FormSelector的表达式,该interface FormSelector<T, U> { entity: Pair<T, U> Select: <K extends keyof T>(this: FormSelector<T, U>, ...properties: K[]) => FormSelector<Omit<T, K>, Pick<T, K> & U> Children: <K extends Filter<T, Array<any>>, P extends keyof ArrayType<T[K]>>( child: K, q: (_: FormSelector<ArrayType<T[K]>, Unit>) => FormSelector<Omit<ArrayType<T[K]>, P>, Pick<ArrayType<T[K]>, P>> ) => FormSelector<Omit<T, K>, U & { [key in K]: Array<Pick<ArrayType<T[K]>, P>> }> } 是嵌套对象的子集。

q

因此,更改Entity()Select()SELECT * FROM reporting_events WHERE (device_id = 51); 的两个表达式签名对我来说解决了这个问题。

我向可能对构建自定义类型安全框架感兴趣的人发布了此答案。也可以随意使用Playground

中的工作版本进行演奏