TypeScript Version:
2.0.3 / nightly (2.1.0-dev.201xxxxx)
Limit is seemingly even lower in 1.8.9, with type information loss after the tuple is 6 or more elements wide.
Code
// A *self-contained* demonstration of the problem follows...
Promise.all([
Promise.resolve(1),
Promise.resolve(''),
Promise.resolve(new Date()),
]).then(results => {
let [
a, // Number
b, // String
c, // Date - Everything is ok!
] = results;
});
Promise.all([
Promise.resolve(1),
Promise.resolve(''),
Promise.resolve(new Date()),
Promise.resolve({}),
Promise.resolve({ hi: 'hello' }),
Promise.resolve([]),
Promise.resolve(null as string),
Promise.resolve(1),
Promise.resolve(''),
Promise.resolve(new Date()),
Promise.resolve({}), // Length == 11
]).then(results => {
let [
a, // Everything is {} now?
b,
c,
d,
e,
f,
g,
h,
i,
j,
k, // << -- Error!
// "Tuple with length 10 cannot be assigned to tuple with length 11"?
] = results;
});
Code on the playground.
Expected behavior:
Tuple should be destructured with the appropriate types.
Actual behavior:
Types are lost after a seemingly arbitrary tuple limit. Actual tuple length is not understood by the compiler.
Workaround:
Break work up into smaller tuples below the limit and assemble them after. This will preserve type information.
This comes from the promise definition, the maximum overload of tuples in the definition is 10. See https://github.com/Microsoft/TypeScript/blob/master/lib/lib.es6.d.ts#L5289.
Tuple types are apparently not inferred anywhere else. Why is that since the types are definitely known in cases such as this?
let x = [1, 'two'];
let [
a, // string | number
b, // string | number
] = x;
Code on the playground.
Tuple types are apparently not inferred anywhere else. Why is that since the types are definitely known in cases such as this?
Because we can't meaningfully distinguish these two cases:
let x = [1, 'two'];
console.log(x.indexOf(1)); // no side effects
let [
a, // string | number
b, // string | number
] = x;
let x = [1, 'two'];
x.sort(); // now what?
let [
a, // string | number
b, // string | number
] = x;
It's also not at all apparent that the fact that you wrote
let x = [1, "two"];
implies that you want it to be illegal to write
x[0] = "one";
You guys can close this issue if you want - no need to leave it cluttering more relevant issues.
I appreciate you taking the time to answer my questions. Everyone has clearly thought through the issues and edge cases here!
@RyanCavanaugh the fact that you can't meaningfully distinguish between them is a problem no matter what though:
// This type checks even though it shouldn't
function bad(foo: [number, string]): [number, string] {
return foo.sort()
}
It seems that while representing tuples as arrays at runtime is fine and dandy, perhaps at compile time they should be kept distinguished, i.e. [number, string] and (number|string)[] should be considered mutually incompatible types.
[number, string] and (number|string)[] should be considered mutually incompatible types.
The example actually typechecks because the signature of sort is
sort(compareFn?: (a: T, b: T) => number): this;
Unlike the example code from #13151, foo in
function bad(foo: [number, string]): [number, string] {
return foo.sort()
}
has a tuple type.
I know, I'm saying that tuples should not be considered subtypes of Array, in which case the sort method wouldn't be available on them. It makes no sense to sort a tuple.
FYI, you can work around this issue for Promises by using an object, rather than an array.
async function promiseObject<T>(obj: {[k in keyof T]: Promise<T[k]>}): Promise<T> {
const keys = Object.keys(obj);
const promises = keys.map(k => (obj as any)[k]);
const values = await Promise.all(promises);
const out = {} as any;
keys.forEach((k, i) => {
out[k] = values[i];
});
return out;
}
here's usage:
describe('promiseObject', () => {
it('should await an object of promises', async () => {
const {a, b} = await promiseObject({
a: Promise.resolve(1),
b: Promise.resolve('b')
});
expect(a).to.equal(1);
expect(b).to.equal('b');
});
});
Most helpful comment
I know, I'm saying that tuples should not be considered subtypes of
Array, in which case thesortmethod wouldn't be available on them. It makes no sense to sort a tuple.