Sep*_*eed 5 arrays types typescript
我有一个从数组中返回一项的函数:
function oneItem(arr: any[]) {
return arr[~~(Math.random() * arr.length)];
}
Run Code Online (Sandbox Code Playgroud)
我希望有合适的类型。
我尝试过的:
type ValuesOf<T extends any[]>= T[number];
function oneItem<ARRAY extends any[]>(arr: ARRAY): ValuesOf<ARRAY> { ... }
const a = oneItem(["foo", "bar"]);
// typeof a is (string) instead of ("foo" | "bar")
Run Code Online (Sandbox Code Playgroud)
在这里找到(https://github.com/Microsoft/TypeScript/issues/20965)
如何从具有正确类型的数组中返回单个项目?
编辑:我想出了只读数组的方法:
type ValuesOf<T extends readonly any[]>= T[number];
function oneItem<ARRAY extends readonly any[]>(arr: ARRAY): ValuesOf<ARRAY> { ... }
const a = oneItem(["foo", "bar"] as const);
Run Code Online (Sandbox Code Playgroud)
当您说“正确”类型时,我想您的意思是您想要编译器可以推断出的最窄的可能类型。
这是否“适当”,仁者见仁智者见智。运行时代码let x = 0在 TypeScript 中可能有多种可能的类型,编译器必须只选择其中一种,除非您自己注释类型。
例如,我可能希望将其解释为let x: number = 0默认编译器行为,并且如果我计划x稍后分配不同的数字,则这是有意义的。或者我可能想要,let x: 0 | 1 = 0因为我只会编写类似的代码x = y & 1,并且我知道只有这些值才会出现。或者我可能想要let x: 0 = 0因为x永远都是0(尽管const在这种情况下我可能会使用)。或者也许我想要let x: number | string = 0,因为有时我会给string它赋值。0或者也许我想要包含作为值的任何其他疯狂类型。
如果您自己不注释它,那么编译器必须推断类型,因此它做出一个假设:非值readonly和非const值将倾向于被推断为“加宽”类型,例如string、number、 或boolean,readonly而 和const值将被推断为“加宽”类型往往被推断为“狭义”文字类型,如"foo“、”0或“ true。
有时编译器的默认假设与开发人员的意图不符。这并不意味着编译器做错了什么;这并不是说它需要并let x = 0推断。这只是意味着开发人员可能需要付出更多努力来传达他或她的意图。stringx
因此,当我编写 时["foo", "bar"],编译器倾向于将其推断为string[]。如果我想要别的东西,我可以自己注释。让我们从这个版本的函数开始:
function oneItem<T>(arr: readonly T[]): T {
return arr[~~(Math.random() * arr.length)];
}
Run Code Online (Sandbox Code Playgroud)
以下是一些输出:
let arr1 = ["foo", "bar"]; // inferred as string[]
const res1 = oneItem(arr1); // string
let arr2: Array<"foo" | "bar"> = ["foo", "bar"];
const res2 = oneItem(arr2); // "foo" | "bar"
let arr3: ["foo", "bar"] = ["foo", "bar"];
const res3 = oneItem(arr3); // "foo" | "bar"
let arr4: readonly ["foo", "bar"] = ["foo", "bar"];
const res4 = oneItem(arr4); // "foo" | "bar"
let arr5 = ["foo", "bar"] as const; // inferred as readonly ["foo", "bar"];
const res5 = oneItem(arr5);
Run Code Online (Sandbox Code Playgroud)
您可以看到arr1使用默认的string[]推断类型,因此string从函数中出来。对于arr2、arr3和arr4,我已将数组注释为逐渐缩小的类型,并且所有这些都允许"foo" | "bar"从函数中出来。最后一个,arr5,使用const断言要求编译器将类型推断为它可以的最窄值,对应于 的类型arr4。
因此,继续的一种方法就是更严格地指定值的类型。
当然,在您的用例中,您有类似的东西
const resArrayLiteral = oneItem(["foo", "bar"]); // string
Run Code Online (Sandbox Code Playgroud)
而你却想"foo" | "bar"出来而不是string。为什么调用者必须要求编译oneItem()器缩小数组文字的类型["foo", "bar"]?实现者不能使用oneItem()某种上下文类型提示来告诉编译器更狭义地解释事物吗?
嗯,有点,是的。只是不太漂亮。这是一种方法:
type Narrowable = string | number | boolean | symbol | object | undefined | void | null | {};
function oneItem<T extends Narrowable>(arr: readonly T[]): T {
return arr[~~(Math.random() * arr.length)];
}
Run Code Online (Sandbox Code Playgroud)
在这里,我们对、、 和 等一系列事物的联合类型进行T约束。事实上,该类型是 的一种乏味版本,因为大多数东西都应该可以分配给它。但给编译器一个提示(请参阅microsoft/TypeScript#10676),如果可能的话,您希望看到推断为文字类型。NarrowablestringnumberbooleanNarrowableunknownNarrowableT
让我们看看它是否有效:
const resArrayLiteral = oneItem(["foo", "bar"]); // "foo" | "bar"
Run Code Online (Sandbox Code Playgroud)
现在看起来不错!T被推断为"foo" | "bar"且不是string。
但正如我所说:这并不漂亮。有一个悬而未决的问题 (microsoft/TypeScript#30680),建议我们能够将oneItem()签名写成类似
function oneI<const T>(arr: readonly T[]): T; // currently invalid syntax
Run Code Online (Sandbox Code Playgroud)
其中const是 在签名中,其行为类似于让调用者使用as const。不过,不确定这个问题是否会得到解决。现在,我们必须像这样跳过障碍Narrowable。
另外,请注意,上述内容T extends Narrowable不会改变 的行为arr1。如果你这样写let arr1 = ["foo", "bar"],那么arr就会被推断为string[]。一旦它是 a string[],编译器就会忘记所有关于"foo"和"bar"的文字类型。并因此而oneItem(arr1)回归string;T将被推断为string. 因此,在某些时候,如果您需要的话,您可能会发现自己需要自己编写窄类型。
好的,希望有帮助;祝你好运!
| 归档时间: |
|
| 查看次数: |
3682 次 |
| 最近记录: |