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

Typescript:数组内对象的类型推断,typescript,type-inference,Typescript,Type Inference,为了对提供给我的一个类构造函数的数据结构进行类型验证,我已经努力了几天,试图完成一些类型脚本正确的推理 本质上,我的构造函数接收一个数组,其中包含一个对象列表,其中包含一个插件声明和一个插件的“自定义配置”,每一个都包含一个 我需要Typescript来确保提供的customConfig与defaultConfig上的类型匹配,但是,我运气不好,甚至没有接近它 我所做的几次尝试都变得非常混乱和荒谬,因此我将附上一个代码的简单表示,我希望这有助于表达我的想法: 我希望有人能提供一些光 类型条目=

为了对提供给我的一个类构造函数的数据结构进行类型验证,我已经努力了几天,试图完成一些类型脚本正确的推理

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

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

我所做的几次尝试都变得非常混乱和荒谬,因此我将附上一个代码的简单表示,我希望这有助于表达我的想法:

我希望有人能提供一些光


类型条目={
分机:{
defaultConfig:Record
安装:任何
卸载:任何
},
customConfig:Record
}
函数initExtensions(a:I):void{/*…*/}
初始化扩展([
{
分机:{
defaultConfig:{foo:true},
安装:()=>{/*…*/},
卸载:()=>{/*…*/},
},
自定义配置:{foo:true}/{/*…*/},
卸载:()=>{/*…*/},
},

customConfig:{bar:true}/您不能完全按照您的要求执行操作,因为TypeScript不支持存在类型

但是,如果您只想验证每个条目是否正确,您可以在每个条目周围创建一个小包装器。该包装器将允许TypeScript推断您的文档中每个条目的类型

type Entry<T, C extends T> = {
    extension: {
        defaultConfig: T
        install: any
        uninstall: any
    },
    customConfig: C
}

function asEntry<T, C extends T>(entry: Entry<T, C>) { return entry };

function initExtensions(entries: Entry<any, any>[]): void { /* ... */ }

initExtensions([
    asEntry({
        extension: {
            defaultConfig: { foo: true },
            install: () => {/* ... */ },
            uninstall: () => {/* ... */ },
        },
        customConfig: { foo: true } // <-- OK
    }),
    asEntry({
        extension: {
            defaultConfig: { bar: 123 },
            install: () => {/* ... */ },
            uninstall: () => {/* ... */ },
        },
        customConfig: { bar: true }  // <-- ERROR
    })
])
类型条目={
分机:{
defaultConfig:T
安装:任何
卸载:任何
},
customConfig:C
}
函数asEntry(entry:entry){returnentry};
函数initExtensions(条目:条目[]):void{/*…*/}
初始化扩展([
入口({
分机:{
defaultConfig:{foo:true},
安装:()=>{/*…*/},
卸载:()=>{/*…*/},
},
自定义配置:{foo:true}/{/*…*/},
卸载:()=>{/*…*/},
},

customConfig:{bar:true}/您不能完全按照您的要求执行操作,因为TypeScript不支持存在类型

但是,如果您只想验证每个条目是否正确,您可以在每个条目周围创建一个小包装器。该包装器将允许TypeScript推断您的文档中每个条目的类型

type Entry<T, C extends T> = {
    extension: {
        defaultConfig: T
        install: any
        uninstall: any
    },
    customConfig: C
}

function asEntry<T, C extends T>(entry: Entry<T, C>) { return entry };

function initExtensions(entries: Entry<any, any>[]): void { /* ... */ }

initExtensions([
    asEntry({
        extension: {
            defaultConfig: { foo: true },
            install: () => {/* ... */ },
            uninstall: () => {/* ... */ },
        },
        customConfig: { foo: true } // <-- OK
    }),
    asEntry({
        extension: {
            defaultConfig: { bar: 123 },
            install: () => {/* ... */ },
            uninstall: () => {/* ... */ },
        },
        customConfig: { bar: true }  // <-- ERROR
    })
])
类型条目={
分机:{
defaultConfig:T
安装:任何
卸载:任何
},
customConfig:C
}
函数asEntry(entry:entry){returnentry};
函数initExtensions(条目:条目[]):void{/*…*/}
初始化扩展([
入口({
分机:{
defaultConfig:{foo:true},
安装:()=>{/*…*/},
卸载:()=>{/*…*/},
},
自定义配置:{foo:true}/{/*…*/},
卸载:()=>{/*…*/},
},

customConfig:{bar:true}/Todd Skelton在回答中演示的方法也是我所知道的唯一一个无样板文件的解决方案。问题是输入数组中的每个条目都是不同的类型(尽管扩展了
条目
),需要在泛型函数签名中清楚地表示,以便正确地进行类型检查。由于目前无法指定具有
n
个模板参数的函数,因此最终会出现如下令人憎恶的情况:

type Entry<C> = {
    extension: { defaultConfig: C };
    customConfig: Partial<C>;
    install: any;
    uninstall: any;
};

type E<C> = Entry<C>;
function initExtensions<C1,C2,C3,C4,C5>(entries: [E<C1>,E<C2>,E<C3>,E<C4>,E<C5>]): void; 
function initExtensions<C1,C2,C3,C4>(entries: [E<C1>,E<C2>,E<C3>,E<C4>]): void; 
function initExtensions<C1,C2,C3>(entries: [E<C1>,E<C2>,E<C3>]): void; 
function initExtensions<C1,C2>(entries: [E<C1>,E<C2>]): void; 
function initExtensions<C1>(entries: [E<C1>]):void; 
function initExtensions(entries:E<any>[]): void {
    // Initialize your extensions here
}
类型条目={
扩展名:{defaultConfig:C};
customConfig:部分;
安装:任何;
卸载:任何;
};
E类=输入;
函数initExtensions(条目:[E,E,E,E,E]):void;
函数initExtensions(条目:[E,E,E]):void;
函数initExtensions(条目:[E,E,E]):void;
函数initExtensions(条目:[E,E]):void;
函数initExtensions(条目:[E]):void;
函数initExtensions(条目:E),所以这有点必要

当然,没有什么可以阻止您生成多达50个此签名的差异:它仅用于类型检查,不会反映在最终输出中。或者,或者逐个传递配置对象:

function initExtension<C>(entry: Entry<C>): void {
    // Initialize one extension here
}

initExtension({
    extension: { defaultConfig: { foo: true } },
    customConfig: { foo: true },
    install: () => null,
    uninstall: () => null,
});
函数initExtension(条目:条目):void{
//在这里初始化一个扩展
}
初始扩展({
扩展名:{defaultConfig:{foo:true}},
customConfig:{foo:true},
安装:()=>null,
卸载:()=>null,
});

这仅仅是Todds方法的一种替代方法。

Todd Skelton在其答案中演示的方法也是我所知道的唯一无样板文件的解决方案。问题是,输入数组中的每个条目都是不同类型的(尽管扩展了
条目
),需要在泛型函数签名中清楚地表示,以便正确地进行类型检查。由于目前无法指定具有
n
个模板参数的函数,因此最终会出现如下令人憎恶的情况:

type Entry<C> = {
    extension: { defaultConfig: C };
    customConfig: Partial<C>;
    install: any;
    uninstall: any;
};

type E<C> = Entry<C>;
function initExtensions<C1,C2,C3,C4,C5>(entries: [E<C1>,E<C2>,E<C3>,E<C4>,E<C5>]): void; 
function initExtensions<C1,C2,C3,C4>(entries: [E<C1>,E<C2>,E<C3>,E<C4>]): void; 
function initExtensions<C1,C2,C3>(entries: [E<C1>,E<C2>,E<C3>]): void; 
function initExtensions<C1,C2>(entries: [E<C1>,E<C2>]): void; 
function initExtensions<C1>(entries: [E<C1>]):void; 
function initExtensions(entries:E<any>[]): void {
    // Initialize your extensions here
}
类型条目={
扩展名:{defaultConfig:C};
customConfig:部分;
安装:任何;
卸载:任何;
};
E类=输入;
函数initExtensions(条目:[E,E,E,E,E]):void;
函数initExtensions(条目:[E,E,E]):void;
函数initExtensions(条目:[E,E,E]):void;
函数initExtensions(条目:[E,E]):void;
函数initExtensions(条目:[E]):void;
函数initExtensions(条目:E),所以这有点必要

当然,没有什么可以阻止您生成多达50个此签名的差异:它仅用于类型检查,不会反映在最终输出中。或者,或者逐个传递配置对象:

function initExtension<C>(entry: Entry<C>): void {
    // Initialize one extension here
}

initExtension({
    extension: { defaultConfig: { foo: true } },
    customConfig: { foo: true },
    install: () => null,
    uninstall: () => null,
});
函数initExtension(条目:条目):void{
//在这里初始化一个扩展
}
初始扩展({
扩展名:{defaultConfig:{foo:tru