是否有可能使所有REST参数都成为可选参数?

function thisIsAfunction<TArgs extends unknown[] = []>(callback: (firstParam: string, ...args: TArgs) => void) { 
    /* Do something */ 
}

// This is okay ✅
thisIsAfunction((type: string, name?: string, id?: number) => {});

// This should cause a type error ❌
// (is it possible to force all args to be optional?)
thisIsAfunction((type: string, name: string, id: number) => {});

Playground link

以下是扩展的示例:

type SelectCommand<TArgs extends unknown[]> = (filter: any, ...args: TArgs) => void;
class DataSource<TSelectArgs extends unknown[] = []> {
    select: SelectCommand<TSelectArgs>;
    constructor(config: {select: SelectCommand<TSelectArgs>}) {
        this.select = config.select;
    }
}

const ds = new DataSource({
    // This should cause a type error for id and type because they are not optional
    // Is it possible to force the rest arguments to be optional?
    select: (filter: any, id: number, type: string) => { /* Do something */ }
})

ds.select('Search', 1, 'type'); // This should work
ds.select('Search'); // And this should always work

Playground link

推荐答案

就我所知,只需键入undefined[]作为其余参数似乎就能达到目的:

function thisIsAfunction<TArgs extends undefined[] = []>(callback: (firstParam: string, ...args: TArgs) => void) { 
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^
    /* Do something */ 
}

// This is okay ✅
thisIsAfunction((type: string, name?: string, id?: number) => {});

// This should cause a type error ❌
// (is it possible to force all args to be optional?)
thisIsAfunction((type: string, name: string, id: number) => {});

Playground link


对于问题的扩展版本,您可以使用mapped tuple(在本例中称为Optionalized):

type Optionalized<T> = { [K in keyof T]: T[K] | undefined };

type SelectCommand<TArgs extends unknown[]> = (filter: any, ...args: Optionalized<TArgs>) => void;

class DataSource<TSelectArgs extends unknown[] = []> {
    select: SelectCommand<TSelectArgs>;
    constructor(config: {select: SelectCommand<TSelectArgs>}) {
        this.select = config.select;
    }
}

const ds = new DataSource({
    select: (filter: any, id?: number, type?: string) => { /* Do something */ }
})

ds.select('Search', 1, 'type'); // This should work
ds.select('Search'); // And this should always work

Playground link

正如@SanBen在 comments 中指出的那样,上例中的Optionalized类型也可以替换为TypeScrip的Partial实用类型.

Typescript相关问答推荐

类型脚本中的Gnomeshell 扩展.如何导入.ts文件

如何使类型只能有来自另一个类型的键,但键可以有一个新值,新键应该抛出一个错误

替代语法/逻辑以避免TS变量被分配之前使用." "

如何创建泛型类型组件来使用React Native的FlatList或SectionList?'

我用相同的Redux—Toolkit查询同时呈现两个不同的组件,但使用不同的参数

如何提取密钥及其对应的属性类型,以供在新类型中使用?

是否有一个版本的联合类型递归地使属性只出现在一个可选选项中?

TS2339:类型{}上不存在属性消息

使用订阅时更新AG网格中的行

错误TS2403:后续变量声明必须具有相同的类型.变量';CRYPTO';的类型必须是';CRYPATO';,但这里的类型是';CRYPATO';

Select 下拉菜单的占位符不起作用

布局组件中的Angular 17命名路由出口

返回嵌套props 的可变元组

如何使用泛型函数参数定义类型脚本函数

Cypress-验证别名的存在或将别名中的文本与';if';语句中的字符串进行比较

如何以类型安全的方式指定键的常量列表,并从该列表派生所挑选的接口?

使用NextJS中间件重定向,特定页面除外

为什么过滤器不改变可能的类型?

在Nestjs中,向模块提供抽象类无法正常工作

使用 TypeScript 在 SolidJS 中绘制 D3 力图