无法使用TypeScript中的可选参数创建动态类型

无法使用TypeScript中的可选参数创建动态类型,typescript,Typescript,正在尝试使以下TypeScript类型表达式正确以具有本地 尝试编写名为getValue(string,defValue?的类型智能函数,如果找不到键,则返回字符串或默认值。函数的类型应为string | typeof defaultValuelookupValue()函数的类型正确,可以支持这一点 在这一点上,我们尝试了该方法的4种不同变体,其中3种在编译或使用中失败,最后一种情况没有完全处理类型输入,但编译了 // This function is good -- correctly ha

正在尝试使以下TypeScript类型表达式正确以具有本地 尝试编写名为
getValue(string,defValue?
的类型智能函数,如果找不到键,则返回字符串或默认值。函数的类型应为
string | typeof defaultValue
lookupValue()函数的类型正确,可以支持这一点

在这一点上,我们尝试了该方法的4种不同变体,其中3种在编译或使用中失败,最后一种情况没有完全处理类型输入,但编译了

//  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");

}

Typescript目前在可选参数和泛型方面有局限性。如果添加与泛型混合的可选参数,编译器始终假定
|未定义
。例如:

function doSomething<A>(param1?: A) {
  return param1;
}
首先,将R和A设置为字符串,默认情况下为未定义。因此,如果R/A没有其他信息可供处理,则编译器会假定R/A为string/undefined。然后必须设置
defvalue:A
,以阻止编译器添加
|未定义的
。如果没有
| undefined
,编译器可以执行类型代数。然后必须指定函数的结果是
R | A
,因为这基本上就是您想要的。下一步是告诉编译器停止根据调用lookupValue的结果推断“A”是什么,因为它将错误地推断。这也是为什么我们需要使用“R”而不仅仅是
string | A
。本质上,如果您没有对lookupValue的结果进行大小写(或使用string | 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;

您可以动态实现如下可选参数:

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
函数doSomething(
类型:T,
…[data]:TypeDataSchema[T]从不扩展?[]:[TypeDataSchema[T]]
) {
}
接口类型数据模式{
“你好”:电话号码
“再见”:永远不会
}
doSomething('bye')//好的
doSomething('bye',25)//应为1个参数,但得到2个
doSomething('hello',5)//好的
doSomething('hello')//应为2个参数,但得到1个

谁是
?还有
T
如何与
相关?理想情况下,我们希望列类型和
T
之间存在关系,否则这比
任何
都好。列是具有附加数据的对象。例如,{name:xxx,address:yyy}。所以键是“name”|“address”“--对于这个例子来说并不重要
 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");
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