我正在尝试在TypeScript中编写一个类似于以下JavaScript示例的简单函数(我的实际用例很复杂,因此我将问题简化为更简单的用例):
function resolve(value, defaultValue=null) {
if (value !== undefined && value !== null) {
return value;
} else {
return defaultValue;
}
}
我有一个函数可以将可空变量解析为它们的实际值。如果参数为null或未定义,则返回默认值。默认值的默认值为null。这种功能的动机是有争议的,所以我们假设这个功能有助于强制执行编码标准等。
让我们观察一下语义:
function resolve(value, defaultValue=null) {
if (value !== undefined && value !== null) {
return value;
} else {
return defaultValue;
}
}
let a = resolve(undefined); // null
let b = resolve(2); // 2
let c = resolve(13, 0); // 13
let d = resolve(null, ''); // ''
let myVar = 'some-value'; // get value from somewhere, could be anything (not just a hard-coded string)
let e = resolve(myVar); // Could be typeof myVar or null
let f = resolve(myVar, 0); // Could be typeof myVar or number (cannot be null)
let g = resolve(myVar, ''); // Could be typeof myVar or string (cannot be null)
如果传入一个nullish值,则会得到默认值。如果未指定默认值,则默认默认值为null。如果传入的默认值不为null,则无法获取null。获取null的唯一方法是显式传递它或完全省略defaultValue
参数。
这似乎是条件类型的一个很好的候选者。但是,我似乎无法纠缠它。这可能是因为基于TypeScript的一些语义,这是不可能的。
让我告诉你我想要实现的目标。以下内容不编译:
function resolve<T, TDefault extends T>(value: T | undefined | null, defaultValue?: TDefault): T | (typeof defaultValue extends undefined ? null : TDefault) {
if (value !== undefined && value !== null) {
return value;
} else {
if (defaultValue !== undefined) {
return defaultValue;
} else {
return null;
}
}
}
let myVar: string | undefined = 'some-value'; // get value from somewhere, could be anything (not just a hard-coded string)
let e = resolve(myVar); // Should return string | null
let f = resolve(myVar, 0); // Should not compile. Number is not assignable to string
let g = resolve(myVar, ''); // Should return string, always.
具体看一下返回类型:
T | (typeof defaultValue extends undefined ? null : TDefault)
我希望你能在这里看到我的目标。如果参数defaultValue
未定义(省略),则返回类型为T | null
,否则返回类型为T | TDefault
(但TDefault
扩展T
,因此ACTUAL返回类型只是T
)。我知道我所拥有的东西不起作用,甚至没有意义。我只是为了演示目的而写的。 typeof defaultValue
相当于写TDefault | undefined
,所以我写的条件类型是分配的,等等。
我尝试了各种不同的东西,但我似乎无法得到它。我想知道TypeScript是否可以捕获此函数的语义。
有什么想法吗?
答案 0 :(得分:3)
我觉得你很努力。类型定义不应该有条件参数,而是指定所有选项。为什么?代码定义逻辑,类型定义仅定义可能的输入和输出。
(typeof defaultValue extends undefined ? null : TDefault)
似乎在操场上编译,但实际上你需要的所有输出是:
function resolve<T>(value: T | undefined | null, defaultValue: T | null = null): T | null {
return value || defaultValue; //for demo purposes
}
由于T已经定义了允许T子类的输入,因此您不需要指定TDefault。因为在执行这个函数之后你不会知道结果是T或T的子类。你唯一能做的就是键入检查:
if(output instanceof SubClassOfT){
(output as SubClassOfT).SomeSpecificStuf
}
答案 1 :(得分:3)
我认为你的用例会因重载而得到更好的服务。
function resolve(value: undefined| null) : null
function resolve<TDefault>(value: undefined| null, defaultValue: TDefault) : TDefault
function resolve<T, TDefault extends T>(value: T, defaultValue: TDefault) : T | TDefault
function resolve<T>(value: T) : T | null
function resolve(value, defaultValue=null) {
if (value !== undefined && value !== null) {
return value;
} else {
return defaultValue;
}
}
let a = resolve(undefined); // null
let b = resolve(2); // number
let c = resolve(13, 0); // This one is an error since 0 does not extends 13, but maybe this should be the behaviour ?
let d = resolve(null, ''); // ''
let myVar = 'some-value'; // get value from somewhere, could be anything (not just a hard-coded string)
let e = resolve(myVar); // string | nul
let f = resolve(myVar, 0); // err
g = resolve(myVar, ''); //string