I effectively would like a typed array that will have only ever have a set number of items — in the example below, 0, 3, or 8.
However, the type of the contents when calling forEach or similar ends up being never
due to the []
.
type O = {prop:number}
type T = []|[O,O,O]|[O,O,O,O,O,O,O,O]
let t:T = []
t.forEach(v=>{
v.prop // <- error, v is type never
})
A reason for the approach is to allow TS to be more aggressive with array index access checking, e.g.
t[0].prop = 1 // ! t[0] is type O|undefined
This may be a bit silly but I stepped on my own foot because T is [] very infrequently and TS array access is permissive. Given I know it's always either empty, length x, or length y, I figured it'd be an easy safety to just spec it.
CodePudding user response:
Edit:
It seems like the workaround is not needed at all. The reason for the error is that TypeScript outsmarts us here. The compiler can see that you assigned an empty array to t
and ignores the explicit type you gave t
. There is hidden type information associated with t
which makes its type just []
and not T
.
We can stop this behavior with a type assertion.
let t: T = [] as T
t.forEach(v=>{
v.prop // number
})
And the error is gone.
The assertion is only needed in this case because you assigned a literal value to t
. If the value of t
is truly unknown because it is generated at runtime or passed via argument, this is not needed.
One possible workaround could be to not use the []
type to represent an empty array. Instead, use an O[]
and intersect it with { length: 0 }
.
type T = (O[] & { length: 0 }) | [O,O,O] | [O,O,O,O,O,O,O,O]
Assigning arrays of various lengths still works as before.
// valid
let t: T = []
t = [{prop: 0}, {prop: 0}, {prop: 0}]
t = [{prop: 0}, {prop: 0}, {prop: 0}, {prop: 0}, {prop: 0}, {prop: 0}, {prop: 0}, {prop: 0}]
// invalid
t = [{ prop: 0 }]
And the error in the forEach
is gone.
t.forEach(v => {
v.prop // number
})
A cleaner approach would probably just to check if t
is empty before using it for the .forEach
.
let t: T = [] as T
t.length !== 0 && t.forEach(v=>{
v.prop // number
})
TypeScript is smart enough the exclude the []
type from the type t
by checking the length
.