如何将typescript泛型限制为两个基元之一



我正在编写一个函数,该函数运行在查找元素的参数列表中。如果找到,函数将去掉名称并返回值,如果没有,则返回默认值。这些值将是numberstring。我想要一种使用typescript泛型和约束的方法来编译以下内容。

function GetValue<T extends number | string>(datum: string, element: string, defaultValue: T): T {
if (datum.startsWith(element)) {
if (typeof defaultValue === 'number')
return parseInt(datum.substring(element.length)); // error
else if (typeof defaultValue === 'string')
return datum.substring(element.length); // error
}
return defaultValue;
}

我已经排除了任何错误检查代码,例如parseInt。这将被称为;

console.log(GetValue("abc123", "abc", 456));
console.log(GetValue("abc123", "def", 456));

并期望得到123456

目前,它在// error行失败,因为可以用扩展stringnumber但不是stringnumber的类型来调用函数。

如何约束泛型T,使其被限制为stringnumber(而不是并集string | number(,然后返回类型与defaultValue的类型匹配?

显式转换为T,以删除此错误

function GetValue<T extends number | string>(datum: string, element: string, defaultValue: T): T {
if (datum.startsWith(element)) {
if (typeof defaultValue === 'number')
return parseInt(datum.substring(element.length)) as T;
else if (typeof defaultValue === 'string')
return datum.substring(element.length) as T;
}
return defaultValue;
}

github上有一个讨论这个主题的问题。

请看看其他方法:

interface Overloading {
<T extends number>(datum: string, element: string, defaultValue: T): number;
<T extends string>(datum: string, element: string, defaultValue: T): string;
}
const GetValue: Overloading = <T extends number | string>(datum: string, element: string, defaultValue: T) => {
if (datum.startsWith(element)) {
if (typeof defaultValue === 'number')
return parseInt(datum.substring(element.length));
else if (typeof defaultValue === 'string')
return datum.substring(element.length);
}
return defaultValue;
}

const res = GetValue('12', 'sdf', 2) // number
const res = GetValue('12', 'sdf', 'sdf') // string
const res = GetValue('12', 'sdf', ()=>{}) // error

有时最好不要明确定义函数的返回类型。让TS帮你做吧。

我已经将此函数重载用于约束。

更新

TS文档:功能过载

//Let's say you have an enum:
const enum State {
idle = 'idle',
active = 'active',
disable = 'disable'
}
//And you have a function:
// function handleState(state: State, payload: string | number | number[]): void;
// But you have next constraints:
// If state is `active`, payload should be only string
// If state is `idle`, payload should be number
// If state is `disable`, payload shoul be array of numbers number[]
// So let's update our handleState function
function handleState(state: State.active, payload: string): void;
function handleState(state: State.idle, payload: number): void;
function handleState(state: State.disable, payload: number[]): void;
function handleState(state: State, payload: string | number | number[]) {}
handleState(State.active, 2) // Error, active state can't be along with number payload
handleState(State.active, '2') // Ok, active state can be along with string payload

要使箭头函数重载,请考虑接口,就像我的第一个例子一样。

您可以像我一样向重载中添加泛型参数。

这就是你要问的吗?

因为在仿制药的情况下:

interface Overloading {
<T extends number>(datum: string, element: string, defaultValue: T): number
<T extends string>(datum: string, element: string, defaultValue: T): string
}

T是数字或字符串,但不是并集。

最新更新