正确键入字符串arrayiffy函数
Properly type the string arrayiffy function
我正在尝试为 arrayiffy
函数设置正确的类型。它的API如下:
- 如果给定一个非空字符串,它会把它放入一个数组中 returns 那
- 如果给定一个空字符串,它returns一个空数组
- 如果给出任何其他内容,将按原样返回
基本类型签名重载有效,但“任何”都不是最优的:
function arrayiffy(something: string): [string];
function arrayiffy(something: string): [];
function arrayiffy(something: any): any {
if (typeof something === "string") {
if (something.length) {
return [something];
}
return [];
}
return something;
}
我尝试设置泛型但失败了 (TS playground link):
type NonString<T> = T extends string ? never : T;
function arrayiffy(something: string): [string];
function arrayiffy(something: string): [];
function arrayiffy<Type>(something: NonString<Type>): Type {
if (typeof something === "string") {
if (something.length) {
return [something];
}
return [];
}
return something;
}
你会如何处理这个问题?谢谢。
type StringInABox<T> =
T extends '' ? [] :
string extends T ? [] | [string] :
T extends string ? [T] :
T;
function arrayiffy<T>(something: T): StringInABox<T>;
function arrayiffy<T>(something: T): [] | [string] | T {
if (typeof something !== 'string') return something;
if (something.length) return [something];
return [];
}
function arrayiffy<T>(something: T): [] | [string] | T
签名只说你会得到这三个东西之一,[]
或[string]
或T
。它没有指定什么时候你会得到每个,而且如果你传入一个已知的字符串值,它并不表示你传入的值将是你找到的字符串数组里面。不过,这是一种相对 type-safe 编写此函数签名的方式——比 (something: any) => any
负载更好。
重载签名 arrayiffy<T>(something: T): StringInABox<T>
确实保证了另一个签名丢失。 StringInABox
条件类型检查 known-to-be-empty 字符串,未知的 string
可能为空也可能不为空(因此 return 值为 [] | [string]
), known-and-not-empty string
直接放入数组 ([T]
) 或其他任何东西,它只是 return 本身。
值得注意的是 string extends T
并不完美——如果你有一个 string & SomethingElse
,它会失败,然后转到 T extends string
,它会通过。结果是 [string & SomethingElse]
,如果对象的 string
部分具有运行时值 ''
(如果它在编译时已知为 '' & SomethingElse
,则不问题,那将属于 T extends ''
桶)。
我们使用此重载是因为 Typescript 不会根据我们对输入类型的值(此处为 T
)使用的运行时检查来缩小条件类型(例如 StringInABox<T>
)的范围。这意味着即使我们知道,Typescript 也知道 something
是一个 string
,Typescript 并不知道 T
是 string
。就 Typescript 而言,即使我们弄清楚 something
是什么,T
仍然是任何东西。这意味着它无法验证 [something]
是否有效 StringInABox<T>
,并会抛出错误。我们要么转换所有 return 值,要么使用重载。我选择了一个重载,因为它是 cleaner-looking,但有一个论点是,在转换时最好是丑陋和明确的(这实际上就是 TS 中的重载)。
我正在尝试为 arrayiffy
函数设置正确的类型。它的API如下:
- 如果给定一个非空字符串,它会把它放入一个数组中 returns 那
- 如果给定一个空字符串,它returns一个空数组
- 如果给出任何其他内容,将按原样返回
基本类型签名重载有效,但“任何”都不是最优的:
function arrayiffy(something: string): [string];
function arrayiffy(something: string): [];
function arrayiffy(something: any): any {
if (typeof something === "string") {
if (something.length) {
return [something];
}
return [];
}
return something;
}
我尝试设置泛型但失败了 (TS playground link):
type NonString<T> = T extends string ? never : T;
function arrayiffy(something: string): [string];
function arrayiffy(something: string): [];
function arrayiffy<Type>(something: NonString<Type>): Type {
if (typeof something === "string") {
if (something.length) {
return [something];
}
return [];
}
return something;
}
你会如何处理这个问题?谢谢。
type StringInABox<T> =
T extends '' ? [] :
string extends T ? [] | [string] :
T extends string ? [T] :
T;
function arrayiffy<T>(something: T): StringInABox<T>;
function arrayiffy<T>(something: T): [] | [string] | T {
if (typeof something !== 'string') return something;
if (something.length) return [something];
return [];
}
function arrayiffy<T>(something: T): [] | [string] | T
签名只说你会得到这三个东西之一,[]
或[string]
或T
。它没有指定什么时候你会得到每个,而且如果你传入一个已知的字符串值,它并不表示你传入的值将是你找到的字符串数组里面。不过,这是一种相对 type-safe 编写此函数签名的方式——比 (something: any) => any
负载更好。
重载签名 arrayiffy<T>(something: T): StringInABox<T>
确实保证了另一个签名丢失。 StringInABox
条件类型检查 known-to-be-empty 字符串,未知的 string
可能为空也可能不为空(因此 return 值为 [] | [string]
), known-and-not-empty string
直接放入数组 ([T]
) 或其他任何东西,它只是 return 本身。
值得注意的是 string extends T
并不完美——如果你有一个 string & SomethingElse
,它会失败,然后转到 T extends string
,它会通过。结果是 [string & SomethingElse]
,如果对象的 string
部分具有运行时值 ''
(如果它在编译时已知为 '' & SomethingElse
,则不问题,那将属于 T extends ''
桶)。
我们使用此重载是因为 Typescript 不会根据我们对输入类型的值(此处为 T
)使用的运行时检查来缩小条件类型(例如 StringInABox<T>
)的范围。这意味着即使我们知道,Typescript 也知道 something
是一个 string
,Typescript 并不知道 T
是 string
。就 Typescript 而言,即使我们弄清楚 something
是什么,T
仍然是任何东西。这意味着它无法验证 [something]
是否有效 StringInABox<T>
,并会抛出错误。我们要么转换所有 return 值,要么使用重载。我选择了一个重载,因为它是 cleaner-looking,但有一个论点是,在转换时最好是丑陋和明确的(这实际上就是 TS 中的重载)。