Typescript:数组内对象的类型推断

col*_*lxi 1 type-inference typescript

我已经努力了好几天,试图完成一些 Typescript 正确的推理,以便对提供给我的一个类构造函数的数据结构实现类型验证。

本质上,我的构造函数接收一个包含对象列表的数组,其中每个对象都包含(某种)插件的声明和插件的“自定义配置”。

我需要 Typescript 来确保提供的customConfig内容与 上的类型匹配defaultConfig,但是,我没有运气,甚至没有接近它。

我所做的几次尝试变得非常混乱和无意义,所以我将附上一个简单的代码表示,我希望它有助于表达这个想法:

我希望有人能提供一些光明


type Entry = {
    extension: {
        defaultConfig: Record<PropertyKey, unknown>
        install: any
        uninstall: any
    },
    customConfig: Record<PropertyKey, unknown>
}


function initExtensions<I extends Entry[]>(a: I): void { /* ... */ }


initExtensions([
    {
        extension: {
            defaultConfig: { foo: true },
            install: () => {/* ... */ },
            uninstall: () => {/* ... */ },
        },
        customConfig: { foo: true } // <-- SHOULD BE OK
    },
    {
        extension: {
            defaultConfig: { bar: 123 },
            install: () => {/* ... */ },
            uninstall: () => {/* ... */ },
        },
        customConfig: { bar: true }  // <-- Should complain as should be a NUMBER
    },
])
Run Code Online (Sandbox Code Playgroud)

art*_*iak 5

我希望下面的解决方案几乎能满足您的需求。

// keeping defaultConfig and customConfig as seperate generics makes TS inference more granular
type EntryRaw<C extends {}, U extends {}> = {
    extension: {
        defaultConfig: C
        install: any
        uninstall: any
    },
    customConfig: U
}

// checks if configs are equal types wise
type ValidEntry<E extends EntryRaw<{}, {}>> = E extends EntryRaw<infer C, infer U> ? C extends U ? U extends C ? E : never : never : never


type ValidEntries<ES extends EntryRaw<{}, {}>[]> =
    ES extends [] ? ES : // recursion condition
    ES extends [infer E, ...infer R] ? // destruct
    E extends EntryRaw<{}, {}> ? // auxiliary check to allow ValidEntry check
    R extends EntryRaw<{}, {}>[] ? // auxiliary check to allow recursive 'call'
    E extends ValidEntry<E> ?
        [E, ...ValidEntries<R>] : // entry ok, recursive 'call'
        // some hacky error reporting
        [{ __INCOMPATABLE_CONFIG__: [E['extension']['defaultConfig'], 'vs', E['customConfig']] } & Omit<E, 'customConfig'>, ...ValidEntries<R>] 
            : never  : never  : never


// I have been not able to make TS happy with single array argument 
function initExtensions<ES extends EntryRaw<{}, {}>[]>(...es: ValidEntries<ES>): void {  
 }

initExtensions(
    {
        extension: {
            defaultConfig: { foo: true },
            install: () => {/* ... */ },
            uninstall: () => {/* ... */ },
        },
        customConfig: { foo: true } // <-- SHOULD BE OK
    },
    {
        extension: {
            defaultConfig: { bar: 123 },
            install: () => {/* ... */ },
            uninstall: () => {/* ... */ },
        },
        customConfig: { bar: true }  // <-- Should complain as should be a NUMBER
    },
)
Run Code Online (Sandbox Code Playgroud)

操场