在TypeScript条件类型中,如何测试函数/泛型参数的存在?

在TypeScript条件类型中,如何测试函数/泛型参数的存在?,typescript,generics,conditional-types,Typescript,Generics,Conditional Types,我试图用TypeScript编写一个简单的函数,类似于下面的JavaScript示例(我的实际用例很复杂,因此我将问题简化为一个更简单的用例): 我有一个函数,用于将可为空的变量解析为它们的实际值。如果参数为null或未定义,则返回默认值。默认值的默认值为null。这样一个函数的动机是有争议的,所以我们假设这个函数有助于实施编码标准或其他东西 让我们观察一下语义: function resolve(value, defaultValue=null) { if (value !== und

我试图用TypeScript编写一个简单的函数,类似于下面的JavaScript示例(我的实际用例很复杂,因此我将问题简化为一个更简单的用例):

我有一个函数,用于将可为空的变量解析为它们的实际值。如果参数为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)
如果传入一个空值,则得到默认值。如果未指定默认值,则默认值为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.
我希望你能明白我的目的。如果参数
defaultValue
未定义(省略),则返回类型为
T | null
,否则返回类型为
T | TDefault
(但是,
TDefault
扩展了
T
,因此实际返回类型只是
T
)。我知道我所拥有的东西不起作用,甚至没有意义。我写这篇文章只是为了说明
typeofdefaultvalue
相当于写入
TDefault | undefined
,因此我编写的条件类型是分布式的,等等。 我试过很多不同的方法,但似乎都不成功。我想知道这个函数的语义是否可能被TypeScript捕获


有什么想法吗?

我想你是在努力学习。类型定义不应具有条件参数,而应指定所有选项。为什么?代码定义逻辑,类型定义仅定义可能的输入和输出

 (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
}

我认为重载会更好地服务于您的用例

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
函数解析(值:未定义| null):null
函数解析(值:未定义| null,默认值:TDefault):TDefault
函数解析(值:T,默认值:TDefault):T | TDefault
函数解析(值:T):T | null
函数解析(值,defaultValue=null){
如果(值!==未定义&&value!==空){
返回值;
}否则{
返回默认值;
}
}
设a=解析(未定义);//无效的
设b=解析(2);//数
设c=resolve(13,0);//这是一个错误,因为0不扩展13,但可能这就是行为?
设d=resolve(null),;/“”
让myVar='一些值';//从某处获取值,可以是任何内容(不仅仅是硬编码字符串)
设e=resolve(myVar);//字符串| nul
设f=resolve(myVar,0);//犯错误
g=解析(myVar,)//一串

大家好,很抱歉耽搁了这么久。我故意提出这个项目,因为我离它太近了。这似乎奏效了。我已经重构了我的方法(这与上面的简化示例略有不同),现在它似乎可以工作并有意义了。这个答案+从项目中休息一下真的很有帮助,谢谢!
function resolve<T>(value: T | undefined | null, defaultValue: T | null = null): T | null {
    return value || defaultValue; //for demo purposes
}
if(output instanceof SubClassOfT){
    (output as SubClassOfT).SomeSpecificStuf
}
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