I have this enum
enum MyTypes {
TypeA = 'TypeA';
TypeB = 'TypeB';
}
and with that enum, I build this record:
const MyRecord: Readonly<Record<MyTypes, string>> = {
[MyTypes.TypeA]: 'end-point-a',
[MyTypes.TypeB]: 'end-point-b'
} as const
Now, I want to build a function that returns 'end-point-a'
or 'end-point-b'
so I can use the returned value on a switch like
const myEndpoint = getEndpoint();
switch(myEndpoint){
case MyRecord.MyTypeA:
///
break;
case MyRecord.MyTypeB:
///
break;
default:
thisFailsWithOtherStuffThanNever(myEndpoint) // This should not fail
}
But I can not figure out how to define "a value inside MyRecord". Is this even possible?
CodePudding user response:
You are not allowed yo use explicit Readonly<...>
type with as const
assertion.
You need to use satisfies
instead. This is new keyword which was introduced in TS 4.9
const MyRecord = {
[MyTypes.TypeA]: 'end-point-a',
[MyTypes.TypeB]: 'end-point-b'
} as const satisfies Readonly<Record<MyTypes, string>>
Now, you have a guarantee that MyRecord
satisfies your type and you have type inference.
Now, you need to create a type which will consist of values of MyRecord
obj.
Let's name it Endpoints
:
enum MyTypes {
TypeA = 'TypeA',
TypeB = 'TypeB',
}
const MyRecord = {
[MyTypes.TypeA]: 'end-point-a',
[MyTypes.TypeB]: 'end-point-b'
} as const satisfies Readonly<Record<MyTypes, string>>
type Values<T> = T[keyof T]
type EndpointMap = typeof MyRecord
type Endpoints = Values<EndpointMap>
The whole code:
enum MyTypes {
TypeA = 'TypeA',
TypeB = 'TypeB',
}
const MyRecord = {
[MyTypes.TypeA]: 'end-point-a',
[MyTypes.TypeB]: 'end-point-b'
} as const satisfies Readonly<Record<MyTypes, string>>
type Values<T> = T[keyof T]
type EndpointMap = typeof MyRecord
type Endpoints = Values<EndpointMap>
function thisFailsWithOtherStuffThanNever(nope: never) {
}
function getEndpoint(): Endpoints {
return MyRecord.TypeA;
}
const myEndpoint = getEndpoint();
const foo = (endpoint: Endpoints) => {
switch (endpoint) {
case MyRecord.TypeA:
///
break;
case MyRecord.TypeB:
///
break;
default:
thisFailsWithOtherStuffThanNever(endpoint)
}
}
CodePudding user response:
You have defined the mapping login in two places:
First place is the MyRecord
const MyRecord: Readonly<Record<MyTypes, string>> = {
[MyTypes.TypeA]: 'end-point-a',
[MyTypes.TypeB]: 'end-point-b'
} as const
The second one is in the swithc/case statement.
const myEndpoint = getEndpoint();
switch(myEndpoint){
///..
}
You have to choose one. I'll propose two solutions to it. I prefer the first one. Playground links proived
Solution one: use that mapping object. Playground here
const endpointMapping: Readonly<Record<string, string>> = {
[MyTypes.TypeA]: 'end-point-a',
[MyTypes.TypeB]: 'end-point-b'
} as const
const myEndpoint = getEndpoint();
const mappedEndpoingValue = endpointMapping[myEndpoint];
if (mappedEndpoingValue !== undefined) {
console.log(`I have foudn the mapping it is ${mappedEndpoingValue}`)
} else {
// Handle it. Probably throw an error
console.error(`Not supportend endpoint type ${myEndpoint}`);
}
Solution two: use the switch/case statement. Pllaygroudn using switch case
const myEndpoint = getEndpoint();
let mappedEndpoingValue: string = "";
switch (myEndpoint) {
case MyTypes.TypeA:
mappedEndpoingValue = "end-point-a"
break;
case MyTypes.TypeB:
mappedEndpoingValue = "end-point-a"
break;
default:
// Handle it. Probably throw an error
console.error(`Not supportend endpoint type ${myEndpoint}`);
throw new Error("not fount mapping")
}
console.log(`I have foudn the mapping it is ${mappedEndpoingValue}`)