Object types in TypeScript are "open" or "extendible", not "closed" or "exact". A value of an object type is required to have known properties, but in general it is not prohibited from having extra, unknown properties. Open and extendible object types are very useful: they allow interface and class extension to form type hierarchies; so interface Bar extends Foo { extraProp: string }
means that every Bar
is also a Foo
, despite Foo
's definition not knowing anything about extraProp
. But it does have implications that are surprising to developers. For now there is no real support for exact types (although excess property checking does make it seem otherwise, sometimes); see microsoft/TypeScript#12936 for the relevant feature request.
Let's examine the type of obj
as inferred by the compiler:
/* const obj: {
A: number;
B: number;
C: number;
} */
That means the compiler knows that obj
has numeric properties at keys A
, B
, and C
. But because object types are open, the compiler does not know that it lacks all other properties. And although the object literal assigned to obj
does indeed lack any other properties, the type system does not keep track of such information. As far as the compiler is concerned, obj
may or may not have all kinds of other properties. It will only let you access A
, B
, and C
without warning (with --strict
or --noImplicitAny
at any rate), but if you index into it with some unknown string
key, the compiler says "that could be any
thing".
The closest TypeScript can get to specifying the types of unknown properties is to use an index signature, like {[k: string]: number | undefined}
.
You can't say "A
, B
, and C
are of type number
and all other properties are undefined
" because that would require a different sort of index signature to represent "all other properties" (see microsoft/TypeScript#17867 for the relevant feature request). Instead you could say "A
, B
, and C
are number
and all properties are number | undefined
. Or, if you don't really care about keeping track of A
, B
, and C
, you could do the above-mentioned "all properties are string | undefined
via the following type annotation:
const obj: {[k: string]: number | undefined} = {
"A": 1,
"B": 2,
"C": 3
};
const f = (str: string) => {
const result = obj[str]; // number | undefined
}
Now your function is known to return number | undefined
, as you wanted.
Playground link to code
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…