type Status = 'Active' | 'Pending' | 'Failed';
type User = {
name: string;
age: number;
status: 'active' // how to get autocomplete, it's lowercase??
} | {
error: string;
id: string;
status: 'failed' // can it be something like Status['Failed']
} |
{
id: string;
status: 'pending'
}
Given Status
as conditional type, sometimes there is need to use the conditional type in another complex conditional type.
Different field structure for each status values.
How to make sure type safety when creating the User
with status
field?
I would like to reuse same type, something like below
const fooBarFn(status: Status) {
if (status === 'Pending') {
return {
id: '123',
status
}
}
// rest of fn
}
Are there any other patterns or best practices for the above?
some solution
type RecordX<K extends keyof any> = {
[P in K]: P;
};
type Status = 'Active' | 'Pending' | 'Failed';
type User<T extends RecordX<Status>> = { // how to default assign
name: string;
age: number;
status: T['Active']
} | {
error: string;
id: string;
status: T['Failed']
} |
{
status: T['Pending']
}
const user: User = { // need the template type parameter
name: 'foobar';
age: 99,
status: 'NotWorking' // not working
}
How to default the first generic parameter, so that user doesn't need to provide the type
CodePudding user response:
You can achieve this by using enum
keyword like this:
enum Status {
ACTIVE = 'ACTIVE',
PENDING = 'PENDING',
FAILED = 'FAILED',
}
type User = {
name: string;
age: number;
status: Status.ACTIVE;
} | {
error: string;
id: string;
status: Status.FAILED;
} |
{
id: string;
status: Status.PENDING;
}
Later on you can do this to check the User
typing.
const user: User = { ... }
if (user.status === Status.PENDING) {
// Do something if status is pending
}
CodePudding user response:
First converted literal Status
to object format
type Intermediate = {
Active: 'Active';
Pending: 'Pending';
Failed: 'Failed';
}
Then assigned as generic type for Status
type RecordX<K extends keyof any> = {
[P in K]: P;
};
type Status = 'Active' | 'Pending' | 'Failed';
type User<T extends RecordX<Status> = RecordX<Status>> = {
name: string;
age: number;
status: T['Active']
} | {
error: string;
id: string;
status: T['Failed']
} |
{
status: T['Pending']
}
const user: User = {
name: 'foobar';
age: 99,
status: 'Active' // autocomplete works
}