问题
This question:
TypeScript: Require that two arrays be the same length?
Asks how to create a function that requires two arrays of the same length.
Here's my attempt at a solution.
type ArrayOfFixedLength<T extends any, N extends number> = readonly T[] & { length: N };
const a1: ArrayOfFixedLength<number, 2> = [1] as const; //expected error
const a2: ArrayOfFixedLength<number, 2> = [1, 2] as const;
function myFunction<N extends number>(array1: ArrayOfFixedLength<any, N >, array2: ArrayOfFixedLength<any, N>) {
return true;
}
myFunction<3>([1, 2, 3] as const, [2, 3, 4] as const);
myFunction<2>([1, 2] as const, [1, 2, 3] as const); //expected error
// However, if you don't specify the array length,
// It fails to error
myFunction([1, 2, 3] as const, [2, 3, 4] as const);
myFunction([1, 2] as const, [1, 2, 3] as const); // error is expected, but there is none.
Playground
As noted, this code only gives the TypeScript error if you explicitly state the generic value N - the length of the arrray.
Why is TypeScript unable to infer the value N from the arguments passed into the function?
回答1:
You need to give a hint to the compiler to expect a tuple type. Otherwise the compiler will widen an array literal like [2, 3, 4] to number[]. The hint usually takes the form of including a tuple type in the type annotation or generic constraint; preferably a union of some tuple type that doesn't get in the way of what you're doing:
function myFunction<N extends number>(
array1: ArrayOfFixedLength<any, N> | [never],
array2: ArrayOfFixedLength<any, N & {}> | [never]) {
return true;
}
The | [never] is a hint. See microsoft/TypeScript#27179 and specifically this comment for more information. I used [never] since I expect that you won't pass any arrays with a never value, so it won't matter in practice if array1 accepts such an array.
Yes, it's ugly. I've asked for an easier way in microsoft/TypeScript#30680, but I don't know if something like that will ever be implemented.
Also, note that for array2 I've replaced N with N & {}. If you don't do that, your function will not serve the purpose of constraining the two arrays to the same length; instead, N will just be inferred as the union of the lengths of the two arrays. Ideally you want only array1 to be used to infer N, and the N in array2 should be "non-inferential". There's an open GitHub issue, microsoft/TypeScript#14829, asking for support for this. The & {} is a technique to lower the inference priority, which suffices to only infer N from array1 and not array2.
Let's see it work:
myFunction([1, 2, 3] as const, [2, 3, 4] as const); // okay
myFunction([1, 2] as const, [1, 2, 3] as const); // error
myFunction([1, 2, 3], [2, 3, 4]); // okay
myFunction([1, 2], [1, 2, 3]); // error
Looks good to me. Okay, hope that helps; good luck!
Playground link to code
来源:https://stackoverflow.com/questions/62206735/why-is-the-generic-value-n-not-inferred-from-arguments-in-this-case