简体   繁体   中英

Typescript prevent generalization of string literal to type

So I have this constant Object

const STUDY_TAGS ={
  InstanceAvailability: { tag: "00080056", type: "optional", vr: "string" },
  ModalitiesinStudy: { tag: "00080061", type: "required", vr: "string" },
  ReferringPhysiciansName: { tag: "00080090", type: "required", vr: "string" },
  NumberofStudyRelatedSeries: {
    tag: "00201206",
    type: "required",
    vr: "number",
  }
};

Now I want to infere the return type of each object based on its vr value, but if I look at typeof STUDY_TAGS all key-value pairs look the same like this:

 InstanceAvailability: {
        tag: string;
        type: string;
        vr: string;
    };

Can I somehow force typescript to keep the string literals instead of generalizing them to the type string? I thought of defining the Object with the type Record<string, {tag: string, type: string, vr: "string" |"number"}> but then the all I get when looking at typeof STUDY_TAGS is

Record<string, {
    tag: string;
    type: string;
    vr: "string" | "number";
}

I am really lost here and have no idea on how to solve this problem. Shouldnt it somehow be possible to infer the return type based on an Object having one of 2 string values? In the end I want to create a function which takes in the object and know the returned type based on the vr value

function doSomething<Type extends "number" | "string">({tag, type, vr} : {tag : string, type : string, vr: Type}) : Type extends "number" ? number : string
{
 if(vr === "string") return "test";
 return 0;
}

I believe function overloading and narrowing could help you. Here's a simplified example:

function doSomething(vr: 'string'): string
function doSomething(vr: 'number'): number
function doSomething(vr: string): string | number {
  if(vr === 'string) return 'Test!';
  return 0;
}

Then, when you call doSomething , TypeScript's typing system can infer the return type, because you told it whenever vr is equal to 'string' , the function returns a string, and whenever vr is equal to 'number' , the function returns a number.

const foo = doSomething('string'); // TypeScript knows 'foo' is a string
const bar = doSomething(2); // TypeScript know 'bar' is a number

Here is a more complete example with your specific use-case on TypeScript's playground.

Thanks for the attempt at helping. My main problem was not getting the type info from the object because it would generalize my string constants to the general type string although iÍ defined my object with Object.freeze(). The solution was to define the object like so: const obj = {a: {test: "hi"}} as const;

I did not find this in the Typescript documentation, so I spent some unneeded hours on this problem. With this solution the rest was easy enough.

You should be able to declare the types 'as const'

const STUDY_TAGS ={
  InstanceAvailability: { tag: "00080056", type: "optional" as const, vr: "string" },
  ModalitiesinStudy: { tag: "00080061", type: "required" as const, vr: "string" },
  ReferringPhysiciansName: { tag: "00080090", type: "required" as const, vr: "string" },
  NumberofStudyRelatedSeries: {
    tag: "00201206",
    type: "required" as const,
    vr: "number",
  }
};

Above I've just added the 'as const' for the type property so you can see the difference. You can add these to any of the properties above to get the exact values rather than their primitive.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM