【发布时间】:2022-01-02 11:29:02
【问题描述】:
背景
作为我正在研究的数据分析库的一部分,我正在创建一组函数,用于从字符串中读取某些类型的值。这个想法本质上是定义一个 CSV 文件的结构,所以我可以将它的每个单元格读取为一个字符串,并且知道它应该是什么类型,然后将该字符串转换为适当类型的值。
一些单元格包含多个值,并被转换为数组。其他单元格包含单个值。
我在这里所做的部分工作是删除与预期不符的值,并记录警告。对于包含数组的单元格,我很高兴结果仍然是一个数组,即使它是空的。但是对于包含单个值的单元格,我想将无效值转换为null。
为此,我设置了一个泛型类型,供所有这些“转换器”函数共享,它返回符合这些要求的条件类型:
type TransformerFn<T> = (value: string, locationIdentifier?: string) => T extends any[] ? T : (T | null);
到目前为止,在我实现的简单案例中,例如将字符串拆分为字符串数组或提取布尔值,这工作得很好。 TypeScript 在解析 TransformerFn<string[]> 或 TransformerFn<boolean> 的条件时没有问题。
但我拥有的其中一个转换器本质上是用于确认列中的每个单元格要么是空的,要么包含字符串 enum 中的值,而这里我遇到了一个问题。
当我将它用作函数参数时,我一直在将该字符串枚举键入为Record<string, string>,它一直运行良好。但是,最近我添加了一些功能来重新编码显然应该是特定枚举值但尚未正确加载的数据。
为了实现这一点,我使用了一个带有约束的泛型类型来表示字符串枚举值的联合:
export function enumValue<E extends string>(enums: Record<string, E>, recodeMap?: Record<string, E>)
这一直很好,直到我尝试添加我之前提到的 TransformerFn 输入。
问题
尽管我的泛型类型 E 有一个约束,即 extends string,这意味着 E extends any[] 永远不会为真,TypeScript 还是无法解析我的泛型条件类型。
这是它给我的错误:
类型'(值:字符串,位置标识符?:字符串|未定义)=> E | null' 不能分配给类型 'TransformerFn'。 'E 型 | null' 不能分配给类型 'E extends any[] ? E : E |无效的'。 类型“null”不能分配给类型“E extends any[]? E : E |空'。
这是我的代码:
type TransformerFn<T> = (value: string, locationIdentifier?: string) => T extends any[] ? T : (T | null);
/**
* Checks that the value, if it exists, is a member of an enum.
*
* If the value does not exist, it is transformed to null.
*
* If a recoding map is passed, and it contains instructions for this value, it is recoded first.
*
* If the value exists but it is not a member of the enum and cannot be recoded,
* a warning will be generated and null will be returned.
*/
export function enumValue<E extends string>(enums: Record<string, E>, recodeMap?: Record<string, E>): TransformerFn<E> {
const enumValues: E[] = Object.values(enums);
function isEnumMember(val: unknown): val is E {
return (enumValues as any[]).includes(val);
}
const transformer: TransformerFn<E> = (value: string, locationIdentifier?: string) => {
if (!value) {
return null;
}
if (isEnumMember(value)) {
return value;
}
if (recodeMap && value in recodeMap) {
const recodedValue = recodeMap[value];
return recodedValue;
}
console.warn(`Value '${value}' does not exist within ${enumValues.join(', ')} (${locationIdentifier})`);
return null;
};
return transformer;
}
最小可重现示例
type TransformerFn<T> = () => T extends any[] ? T : null;
function enumValue<E extends string>(): TransformerFn<E> {
const transformer: TransformerFn<E> = () => {
return null;
};
return transformer;
}
类型“() => null”不可分配给类型“TransformerFn”。 类型“null”不能分配给类型“E extends any[]? E : null'。
现在我当然可以放弃并为我的enumValue 函数的返回值使用非条件类型,因为我知道它应该是E | null,而不是尝试使用我的TransformerFn 条件类型。我的代码仍然可以正常工作,实际上不会对我造成任何维护问题。
但是我遇到了一些我不理解并且无法弄清楚的事情。那么,谁能向我解释为什么这不起作用,如果有什么我可以做的事情会起作用吗?
【问题讨论】:
标签: typescript typescript-generics conditional-types