const main = ([a_, b_, c_]: [a, b, c, a]) => {
type SomeTuple = [a, b, c, a];
const X: a = a_;
const Y: Exclude // =
console.log(X, Y);
}
Except nothing solves this because a, b, and c can all be the same. After trying to express this correctly, I ended up with something that appears useable (but that still uses assertions and doesn't really express the type of Y correctly). type Narrowable = string | number | bigint | boolean;
/*
Express the type of a value in a tuple that is not the type of the second parameter
For example:
- ValOfTupleExceptFor -> 6
- ValOfTupleExceptFor -> 1
*/
type ValOfTupleExceptFor = Tup extends [infer First, ...(infer Rest extends Narrowable[])]
? First extends Val
? Rest extends []
? never
: ValOfTupleExceptFor
: First
: never;
const NO_SOLUTION: unique symbol = Symbol('NO_SOLUTION')
const getValOfTupleExcluding = (tup: readonly Narrowable[], val: (typeof tup)[number]): ValOfTupleExceptFor => {
const [first, ...rest] = tup;
if (!first) {
return NO_SOLUTION as never;
}
if (first === val) {
return getValOfTupleExcluding(rest, val);
}
return first as ValOfTupleExceptFor;
}
const main = ([a_, b_, c_]: [a, b, c, a]) => {
const someTuple = [a_, b_, c_, a_] as const;
const X: a = a_;
// This still resolves to type 'never'
const Y: ValOfTupleExceptFor = getValOfTupleExcluding(someTuple, a_);
console.log(X, Y);
}
Which really highlights how powerful the 'planner' style program is in terms of simplicity and conciseness. I guess Typescript isn't even powerful enough to express this kind of constraint.edit: TS playground link with some experiments if anyone's interested: http://tinyurl.com/3p2pzdtn