Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/fortran/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 如何键入此合并函数?_Typescript - Fatal编程技术网

Typescript 如何键入此合并函数?

Typescript 如何键入此合并函数?,typescript,Typescript,我对类型脚本或其他类型语言中标准类型以外的任何东西都比较陌生,我正在尝试找出一种键入此函数的好方法。我想去掉参数和返回类型的任何类型 函数返回的第一个值不是null、未定义或NaN /** returns the first not-null value excluding NaN */ const coalesce = (...args: any): any => { for (let i = 0; i < args.length; i++) { // args[i]

我对类型脚本或其他类型语言中标准类型以外的任何东西都比较陌生,我正在尝试找出一种键入此函数的好方法。我想去掉参数和返回类型的任何类型

函数返回的第一个值不是null、未定义或NaN

/** returns the first not-null value excluding NaN */
const coalesce = (...args: any): any => {
  for (let i = 0; i < args.length; i++) {
    // args[i] === args[i] is to avoid NaN, because NaN !== NaN
    if (args[i] != null && args[i] === args[i]) {
      return args[i];
    }
  }
  return null;
};

最简单的版本是捕获我们在一个类型中传递的参数类型的类型,并返回所有参数类型的并集

我们可以使用中的元组将所有参数的类型捕获为元组类型,并获得元组中所有类型的并集:

const coalesce = <T extends any[]>(...args: T): T[number] => {
    for (let i = 0; i < args.length; i++) {
        // args[i] === args[i] is to avoid NaN, because NaN !== NaN
        if (args[i] != null && args[i] === args[i]) {
            return args[i];
        }
    }
    return null;
};


// o is  string | number | null | undefined under strict null checks
// T is [null, undefined, number, string] so T[number] is string | number | null | undefined
let o = coalesce(null, undefined, NaN, 'maybe')  

最简单的版本是捕获我们在一个类型中传递的参数类型的类型,并返回所有参数类型的并集

我们可以使用中的元组将所有参数的类型捕获为元组类型,并获得元组中所有类型的并集:

const coalesce = <T extends any[]>(...args: T): T[number] => {
    for (let i = 0; i < args.length; i++) {
        // args[i] === args[i] is to avoid NaN, because NaN !== NaN
        if (args[i] != null && args[i] === args[i]) {
            return args[i];
        }
    }
    return null;
};


// o is  string | number | null | undefined under strict null checks
// T is [null, undefined, number, string] so T[number] is string | number | null | undefined
let o = coalesce(null, undefined, NaN, 'maybe')  

好,这实际上是一个相当复杂的类型函数


首先,
NaN
提出了一个问题。TypeScript没有对的表示。值
NaN
的类型为
number
。因此,当您看到类型为
number
的值时,它可能是
NaN
,编译器无法阻止这种情况。因此,当我看到一个
number
参数时,为了
coalesce()
的目的,我必须将其视为类似于
number | null
。因此:

function hmm(n: number) { return coalesce(n, "oops"); }
应返回类型为
number |“oops”
的值,而不仅仅是
number
。我可以通过明确认识到数值文本不能是
NaN
,对其进行一些改进,以便

coalesce(123, "oops");
应计算为
123
而不是
123 |“oops”
,因为
123
是已知的非
NaN
数字


因此,如果您将参数处理为
coalesce()
,那么您需要的是从左到右遍历元组,建立元组中类型的并集,去掉任何可能的
null
未定义的类型。如果您遇到的类型肯定不是
null
未定义的
、或
编号
,则可以停止。否则,您将结束,将
null
添加到您的联合,然后停止

这种类型自然是通过TypeScript进行递归的。有很多方法可以解决这个问题,但我确信唯一一种可以处理的方法是将递归类型展开到某个固定的深度,然后退出

让我们开始编写类型:

// Head<L>: return the first element of a tuple L
// Head<[string, number, boolean]> ~ string
type Head<L extends any[]> = ((...l: L) => void) extends
  ((h: infer H, ...t: infer T) => void) ? H : never;

// Tail<L>: return the tuple L with the first element removed
// Tail<[string, number, boolean]> ~ [number, boolean]
type Tail<L extends any[]> = ((...l: L) => void) extends
  ((h: infer H, ...t: infer T) => void) ? T : never;

// MightSkip<T>: return unknown if coalesce() might possibly skip a value 
//  of this type; return never if coalesce() will definitely stop at a value
//  of this type.  
// MightSkip<string | null> ~ unknown (might be null)
// MightSkip<string> ~ never
// MightSkip<number> ~ unknown (it might skip because NaN)
// MightSkip<123> ~ never (it knows 123 is not NaN)
type MightSkip<T> =
  null extends T ? unknown :
  undefined extends T ? unknown :
  [T] extends [number] ? (number extends T ? unknown : never) :
  number extends T ? unknown :
  never;
请注意,每个
FirstNonNull1
FirstNonNull2
,etc类型都只引用下一个类型,因此它不再是循环的。在使用剩余类型的并集退出更长的参数列表之前,上述方法应该适用于长度为7左右的任何元组。如果你期望更长的参数列表,你可以很容易地扩展上面的方案


差不多了:为了捕获像
123
这样的文本类型的参数,而不让它们自动扩展到像
number
这样的非文本类型,我们需要使用像
可缩小的
这样的类型

type Narrowable = string | number | boolean | symbol | object |
  null | undefined | void | ((...args: any[]) => any) | {};
最后,让我们键入
coalesce()
,看看编译器告诉我们什么:

/** returns the first not-null value excluding NaN */
const coalesce = <T extends Narrowable[]>(...args: T): FirstNonNull<T> => {
  for (let i = 0; i < args.length; i++) {
    // args[i] === args[i] is to avoid NaN, because NaN !== NaN
    if (args[i] != null && args[i] === args[i]) {
      return args[i] as any; // assert
    }
  }
  return null as any; // assert
};
这看起来和我想象的一样好
v0
只是
“嘿”
,而不是
“嘿”|“你”
,因为
coalesce()
肯定会在
“嘿”
处停止
v1
number |“maybe”
,因为编译器将
NaN
视为
number
,所以没有更好的了
v2
“嘿”|“你”| 123
,因为前两个参数中的任何一个可能为null,但
123
肯定不是,所以它必须是这三个参数中的一个。而
v3
null
,因为它从参数列表的末尾掉了下来



好,这实际上是一个相当复杂的类型函数


首先,
NaN
提出了一个问题。TypeScript没有对的表示。值
NaN
的类型为
number
。因此,当您看到类型为
number
的值时,它可能是
NaN
,编译器无法阻止这种情况。因此,当我看到一个
number
参数时,为了
coalesce()
的目的,我必须将其视为类似于
number | null
。因此:

function hmm(n: number) { return coalesce(n, "oops"); }
应返回类型为
number |“oops”
的值,而不仅仅是
number
。我可以通过明确认识到数值文本不能是
NaN
,对其进行一些改进,以便

coalesce(123, "oops");
应计算为
123
而不是
123 |“oops”
,因为
123
是已知的非
NaN
数字


因此,如果您将参数处理为
coalesce()
,那么您需要的是从左到右遍历元组,建立元组中类型的并集,去掉任何可能的
null
未定义的类型。如果您遇到的类型肯定不是
null
未定义的
、或
编号
,则可以停止。否则,您将结束,将
null
添加到您的联合,然后停止

这种类型自然是通过TypeScript进行递归的。有很多方法可以解决这个问题,但我确信唯一一种可以处理的方法是将递归类型展开到某个固定的深度,然后退出

让我们开始编写类型:

// Head<L>: return the first element of a tuple L
// Head<[string, number, boolean]> ~ string
type Head<L extends any[]> = ((...l: L) => void) extends
  ((h: infer H, ...t: infer T) => void) ? H : never;

// Tail<L>: return the tuple L with the first element removed
// Tail<[string, number, boolean]> ~ [number, boolean]
type Tail<L extends any[]> = ((...l: L) => void) extends
  ((h: infer H, ...t: infer T) => void) ? T : never;

// MightSkip<T>: return unknown if coalesce() might possibly skip a value 
//  of this type; return never if coalesce() will definitely stop at a value
//  of this type.  
// MightSkip<string | null> ~ unknown (might be null)
// MightSkip<string> ~ never
// MightSkip<number> ~ unknown (it might skip because NaN)
// MightSkip<123> ~ never (it knows 123 is not NaN)
type MightSkip<T> =
  null extends T ? unknown :
  undefined extends T ? unknown :
  [T] extends [number] ? (number extends T ? unknown : never) :
  number extends T ? unknown :
  never;
请注意,每个
FirstNonNull1
FirstNonNull2
,etc类型都只引用下一个类型,因此它不再是循环的。在使用剩余类型的并集退出更长的参数列表之前,上述方法应该适用于长度为7左右的任何元组。如果你期望更长的参数列表
const v0 = coalesce(null, undefined, "hey", "you"); 
// const v0: "hey"

// TypeScript doesn't have a NaN literal, so NaN is treated as number
const v1 = coalesce(null, undefined, NaN, 'maybe'); 
// const v1: number | "maybe"

const coinFlip = () => Math.random() < 0.5
const v2 = coalesce(coinFlip() ? "hey" : null, coinFlip() ? "you" : undefined, 123);
// const v2: "hey" | "you" | 123

const v3 = coalesce(undefined, undefined)
// const v3: null;