Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
266 views
in Technique[技术] by (71.8m points)

typescript - Automatically infer variable belongs in key set

I have an object and want to access it using a string variable, however I get the error:

no index signature with a parameter of type 'string' was found on type '{ A: number; B: number; C: number; }'

const obj = {
    "A":1,
    "B":2,
    "C":3
 }

 const f=(str:string)=>{
     const result = obj[str];
 }

However, shouldn't this compile and result have type number|undefined? Is the only way to overcome this to cast str to keyof typeof obj as described here? I also thought of doing if (Object.keys(obj).includes(str))... but that does not narrow the type of str to just valid keys.

question from:https://stackoverflow.com/questions/65874824/automatically-infer-variable-belongs-in-key-set

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 anything".

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...