I have a list of paramters that can be given to a function 'one' | 'two' | 'three' | 'four' | 'five'
. I want each to be used only once, like so:
function foo(...x: ('one' | 'two' | 'three' | 'four' | 'five')[]) {
// do something...
}
foo('one', 'five', 'three', 'five'); // This works, even though I want TypeScript to say this isn't allowed.
Typescript 1.4 introduced Union Types so the answer now is yes, you can. function myFunc (param: string [] | boolean [] | number []): void; Using other type than the ones specified will trigger a compile-time error. If you want an array of multiple specific types, you can use Union Types for that as well:
Use a string literal type to only allow specific string values using a TypeScript type, e.g. const str: 'draft' | 'sent' = 'draft';. String literals allow us to refer to specific strings in type positions. If the specified string is not in the literal type, an error is thrown.
In a method declaration in TypeScript, the parameter could be of type array of strings, booleans, or numbers. Do I have to declare it as any [] or is there a way to limit the input type as on of these three types? Typescript 1.4 introduced Union Types so the answer now is yes, you can.
From TypeScript 1.4 seems that it is possible to declare multiple possible types for a function parameter like this: class UtilsClass { selectDom (element: string | HTMLElement):Array<HTMLElement> { //Here will come the "magic-logic" } } This is because of the new TypeScript concept of "union-types". You can see more here.
The comment is correct: it will be nearly impossible to guarantee that only unique arrays will make it through, so no matter what you should have runtime code in place to de-duplicate any array passed in no matter what you do with the typing.
Still, you might want the typing to give users hints that you should only pass in each parameter type at most once. This is possible and not even completely crazy with TypeScript 4.1's support for recursive conditional types:
type UniqueFrom<T extends any[], U> =
U[] extends T ? T :
T extends [infer F, ...infer R] ?
[U, ...UniqueFrom<R, Exclude<U, F>>] : []
type Vals = 'one' | 'two' | 'three' | 'four' | 'five';
function foo<T extends Vals[]>(...t: UniqueFrom<T, Vals>) { }
The idea is that UniqueFrom<T, Vals>
should produce a tuple of the same length as T
, showing which elements from Vals
are still available at each step as you walk through T
from left to right. for example:
type U = UniqueFrom<["one", "two", "three"], Vals>;
// [Vals, "two" | "three" | "four" | "five", "three" | "four" | "five"]
This says that the first element of T
can be anything from Vals
; once the first element is chosen to be "one"
, now the next element can be anything from Vals
except "one"
. When the second element is chosen to be "two"
, now the last element can be anything from "three"
, "four"
, or "five"
.
As long as T
is assignable to UniqueFrom<T, Vals>
, then the compiler will accept a tuple of type T
as a rest argument to foo()
. Let's see if it works:
foo("one", "two", "three", "four", "five"); // okay
foo("two", "one", "six", "five"); // error!
// -------------> ~~~~~
// type '"six"' is not assignable to type 'Vals'
foo("one", "two", "three", "two", "five"); // error!
// ----------------------> ~~~~~
// type '"two"' is not assignable to type '"four" | "five"'
This looks reasonable to me. The compiler complains about a second parameter named "two"
, and tells you that it was expecting either "four"
or "five"
there.
Playground link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With