Reputation: 51
I've got all the strict flags that I would expect turned on, so I was confused when Typescript accepted the following code:
export class Test {
private records: {
[id: string]: number
};
constructor() {
this.records = {
"hello": 1,
"world": 3
}
}
public get(id: string): number {
return this.records[id];
}
}
The get
property obviously does not always return a number: it will return undefined
when the function is passed anything besides "hello"
and "world"
. Is there a flag that would catch this function and require its return type to be number | undefined
, which is the correct return type?
I'm using noImplicitAny
and strictNullChecks
already. Surely there's some way to do this, as I have a function that claims to return a number
returning undefined
!
Upvotes: 4
Views: 5489
Reputation: 51
Based on jonrsharpe's comments, I eventually found this thread. The answer as I understand it is that this type:
interface Wha {
foo: number;
bar: number;
[id: string]: number;
};
is always incorrect, because the type system treats it like it is an object that maps every single id
to a defined number, and no such object can ever exist. (Every object that you can possibly create will always have strings that don't map to numbers, as lilezek pointed out in comments.)
Typescript is perfectly happy giving this object the Wha
type:
let r: Wha = { foo: 4, bar: 7, baz: 33 };
Because it incorrectly thinks that every string
index in a Wha
object maps to a defined number
, Typescript will treat r.notafield
, r.fnord
, and r["ha"]
as having type number
. Therefore, it will happily proceed as if all of these things cannot possibly be undefined. But they all are undefined
! Avoiding that situation is why I wanted to use Typescript with --strictNullChecks
in the first place 😐
If you want to avoid having a bunch of undefined
things zipping around the typechecker pretending to be a totally-defined number
, the only option is this:
interface Wha {
foo: number;
bar: number;
[id: string]: number | undefined;
};
This leads to all sorts of problems, because it means that you can have valid keys in the array that mapped to undefined, where what you originally meant was that, if k
is a key in Object.keys(r)
, then r[k]
is a number
. So there's no good option here.
Maybe the lesson here is that I should use ES6 Maps instead of indexable types, because indexable types lie to you without warning.
Upvotes: 1
Reputation: 7344
Try with this:
public get(id: string): number | undefined {
return this.records[id];
}
Or this:
public get(id: string) {
return this.records[id] as number | undefined;
}
One of these should explicitly return undefined as a possible type.
Upvotes: 0