尝试正确获取以下TypeScript类型表达式以使其具有本地
尝试编写一个名为getValue(string, defValue?)
的类型智能函数,如果找不到该密钥,则该函数将返回字符串或默认值。该函数的类型应为string | typeof defaultValue
,lookupValue()函数应具有正确的类型以支持此功能。
在这一点上,尝试了4种不同的方法,其中三种在编译或使用中失败,最后一种情况不能完全处理类型输入,但可以编译。
// This function is good -- correctly handling the types
lookupValue<D>(record: string[], key: string, defvalue: D): D | string {
const index = this.columnHeaders[key];
const v = index !== undefined ? record[index] : undefined;
return v === undefined || v === "" ? defvalue : v.trim();
}
someFunction(record: string[]) {
// -- Test 1
const getValue = <T>(key: keyof typeof COLUMNS, defvalue = undefined) => lookupValue(record, key, defvalue);
// Argument of type '""' is not assignable to parameter of type 'undefined'.
const bigDef = getvalue("testBig", "something");
// -- Test 2
// Type 'undefined' is not assignable to type 'T'.
const getValue = <T>(key: keyof typeof COLUMNS, defvalue: T = undefined) => lookupValue(record, key, defvalue);
// -- Test 3
// Won't compile since the defvalue is "T | undefined" which isn't valid
const getValue = <T>(key: keyof typeof COLUMNS, defvalue?: T) => lookupValue(record, key, defvalue);
// -- Test 4
// Compiles but is wrong since the following getValue "works"
const getValue = <T = undefined>(key: keyof typeof COLUMNS, defvalue?: T) => lookupValue(record, key, defvalue as T);
// Works - but shouldn't
const foo: string = getValue("test");
}
目标是要满足以下要求:
const big = getvalue("testBig"); // Should be type of string | undefined
const bigDef = getvalue("testBig", "something"); // Should be type string
答案 0 :(得分:1)
Typescript目前在可选参数和泛型方面有局限性。如果您添加了一个与泛型混合使用的可选参数,则编译器将始终假设<generic> | undefined
。例如:
function doSomething<A>(param1?: A) {
return param1;
}
在这种情况下,param1为“ A | undefined”。这会破坏所有类型的流程,因为通用方面不包含未定义方面,而是坚持使用了它-即使在您不带参数调用doSomething()
的情况下也是如此。所以doSomething(“ jello”)的结果是string | undefined
;这很愚蠢,因为人们会认为这将是字符串。对于doSomething()
,返回类型为{} | undefined
而不是undefined
的情况也是如此。
有时,您可以通过用大型隐喻锤子严重击打Typescript来解决此问题。通常,锤子包括制定正确的类型定义,然后投射所有抱怨的内容。没有一种适合所有人的尺码,但您可以这样做:
const getValue = <R = string, A = undefined>(key: string, defvalue: A = (undefined as unknown) as A): R | A =>
(lookupValue(record, key, defvalue) as unknown) as R | A;
首先,将R和A设置为字符串,并且默认情况下未定义。这样一来,如果编译器没有其他可用信息,则假定R / A是字符串/未定义。然后,必须设置defvalue: A
来阻止编译器添加| undefined
。如果没有| undefined
,则编译器可以键入代数。然后,您必须指定函数的结果为R | A
,因为这基本上就是您想要的。下一步是告诉编译器停止基于对lookupValue的调用结果来推断“ A”是什么,因为它将错误地进行推断。这也是为什么我们需要使用“ R”而不是string | A
的原因。本质上,如果您不区分lookupValue的结果(或使用字符串| A作为结果类型),则编译器足够聪明,可以看到类型信息不足,因此,根据情况,“ A”为“未定义”或“字符串”如果将getValue
的返回类型设置为string | A
,则将结果插入到结果中并不能编译(如果省略了强制转换)或失败,如下所示:
const result: string = getValue("testBig");
“ A”会推断出错误的字符串,因为它应该是编译错误。“”无法分配字符串|未定义为字符串”。另一种情况:
const result: string = getValue("testBig");
A
会推断为undefined
,这意味着const result
的类型为string | undefined
,这也是错误的。
为避免上述情况,我们在第二行添加as unknown) as R | A
以获得:
const getValue = <R = string, A = undefined>(key: string, defvalue: A = (undefined as unknown) as A): R | A =>
(lookupValue(record, key, defvalue) as unknown) as R | A;
我能想到的所有情况下都能正常工作
// ss is number | string
const ss = getValue("testBig", 1);
// bigDef is string
const bigDef = getValue("testBig", "something");
// sdf is string | undefined
const sdf = getValue("testBig", undefined);
const sdf = getValue("testBig");
// Compile error -> string | undefined can't be assigned to string
const asdfa: string = getValue("testBig");
答案 1 :(得分:0)
您可以实现如下所示的动态可选参数:
function doSomething<T extends keyof TypeDataSchema>(
type: T,
...[data]: TypeDataSchema[T] extends never ? [] : [TypeDataSchema[T]]
) {
}
interface TypeDataSchema {
'hello': number
'bye': never
}
doSomething('bye') // ok
doSomething('bye', 25) // expected 1 argument but got 2
doSomething('hello', 5) // ok
doSomething('hello') // expected 2 argument but got 1