我试图实现一个函数parseTSV()
,它解析TSV文件并返回一个对象数组,但我很难弄清楚如何键入该函数。
这是我目前掌握的情况:
export const parseTsv = <T>(tsvFilepath: string) => {
const tsv = fs.readFileSync(tsvFilepath, 'utf8');
const lines = tsv.split('\n');
const result: T[] = [];
const columns = lines[0].split('\t'); // How can I constrain columns to be a key of T?
for (let i = 2; i < lines.length; i++) {
const obj = {} as T;
const currentline = lines[i].split("\t");
for (let j = 0; j < columns.length; j++) {
obj[columns[j]] = currentline[j]; /* ERROR: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'unknown'.
No index signature with a parameter of type 'string' was found on type 'unknown'. */
}
result.push(obj);
}
return result;
}
我在编写函数时希望调用者定义<T>
,它是从TSV文件解析的对象的接口(即TSV文件中的列)。
1条答案
按热度按时间wyyhbhjk1#
调用签名
无法安全地实现。它声称接受
tsvFilepath
字符串并生成由调用方在设计时提供的T
类型的值数组。但当TS编译为JS时,静态类型系统被擦除,并且T
只是设计时类型,而不是运行时值。运行的函数没有关于T
的信息,所以如果它真的起作用了那也是巧合您将依赖于 * 开发人员 * 来验证返回的类型是否正确。请考虑以下TypeScript代码:
在运行时,几乎可以肯定
其中
parseTsv("somePath")
运行两次,具有相同的输入,而且我认为可以安全地假设它们将返回相同的输出。没有涉及到Foo
或EvilFoo
。"somePath"
处的文件表示 *Foo
对象数组 * 和 *EvilFoo
对象数组的几率相当低(在bar
属性中同时包含string
和number
的对象是不可能的),因此很可能至少有一个map()
方法将尝试取消引用undefined
,并且您将得到一个运行时错误。实际上,您的
parseTsv()
实现甚至不能生成一个具有非string
属性的类型,而T
是完全不受约束的。但是,即使我们将T
约束为只有string
属性,这个函数不知道它能得到什么键。给你的parseTsv()
一个调用签名的唯一安全的方法是非泛型的:这意味着输出将是属性为
string
或undefined
的一些对象的数组。如果您希望在运行时保持相同的实现和相同的类型,那么我不会太担心实现中的类型安全;只需使用类型Assert或
any
类型来避免警告:如果你想要更安全的类型,你需要改变你的调用签名和实现。一个可能的方法是:
这里你传递的是你希望对象上存在的
keys
的列表。这个函数只在这些键的类型K
中是泛型的。输出类型是{ [P in K]: string }[]
,意味着一个元素数组,其键是K
,值是string
s。我在实现中使用了一些类型Assert来防止错误(例如
as K[]
和as {[P in K]: string}
),这意味着我已经自己承担了保证类型安全的责任。我已经尝试过这样做,如果键不匹配或者如果一行缺少一个条目,则让实现抛出错误。唯一可能的检查方法是,在运行时,类型K[]
的keys
数组存在(而K
本身仅在设计时存在)。无论如何,让我们用一个虚拟文件系统来测试它:
首先是快乐案例:
这是可行的,因为TSV中有
"bar"
和"baz"
密钥。您可以确信,假设objects
是Foo[]
类型,您执行的任何操作都将有效。这些操作失败的原因是
"somePath"
处的文件没有"other"
和"key"
标头,并且"badFile"
处的文件是坏的,这两个都是在运行时捕获的。others.map()
和oops.map()
行永远不会到达。假设others
的类型是Other[]
,oops
的类型是Foo[]
是没有问题的,因为代码运行的唯一方法是满足这些约束,所以它是合理的类型安全的。Playground代码链接