Home > Software design >  Typescript discriminate union where field can be anything but the other discriminant field
Typescript discriminate union where field can be anything but the other discriminant field

Time:12-03

How do I create a discriminated union where I can check either for a statusCode of '0000' or not '0000', so that the correct object type is used?

type Foo = {
  statusCode: '0000',
  something: string
} | {
  statusCode: // any string that is not '0000'
  somethingElse: string
  }

CodePudding user response:

You can use something like this. This approach is very verbose.

type SuccessResponse = {
  statusCode: '0000';
  data: {
    id: string;
    name: string;
  };
};

type ErrorResponse = {
  statusCode: '0001' | '0002' | '0003';
  error: {
    message: string;
  };
};

type Response = SuccessResponse | ErrorResponse;

const response: Response = {
  statusCode: '0000',
  data: {
    id: '123',
    name: 'John Doe',
  },
};

if (response.statusCode === '0000') {
  const successResponse = response as SuccessResponse;
  console.log(successResponse.data.name);
} else {
  const errorResponse = response as ErrorResponse;
  console.log(errorResponse.error.message);
}

CodePudding user response:

Use type branding to make such a type and function checking it

type Foo = {
    statusCode: '0000',
    something: string
} | {
    statusCode: string & { _not: '0000' } // any string that is not '0000'
    somethingElse: string
}

function whatFoo(foo: Foo) {
    if (foo.statusCode == '0000') {
        return foo as Extract<Foo, { statusCode: '0000' }>
    } else {
        return foo as Exclude<Foo, { statusCode: '0000' }>
    }
}
// ^?
// function whatFoo(foo: Foo): {
//     statusCode: "0000";
//     something: string;
// } | {
//     statusCode: string & {
//         _not: "0000";
//     };
//     somethingElse: string;
// }

CodePudding user response:

In TypeScript, you can use a discriminated union to represent a type that can have one of several possible shapes. Each shape can have different fields, but at least one field is common to all of the shapes and is used to discriminate between them.

To create a discriminated union where one of the shapes has a statusCode of '0000' and the other shape has any statusCode that is not '0000', you can use the never type for the statusCode field in the first shape. This will ensure that the statusCode field can only be '0000' in that shape. Here's an example:

type Foo =
  | { statusCode: '0000', something: string }
  | { statusCode: Exclude<'0000', string>, somethingElse: string }

In this example, the Foo type has three possible shapes: a shape with a statusCode of '0000' and a something field, a shape with a statusCode of '0000' and a something field (this is included to enforce the '0000' value for the statusCode field), and a shape with any statusCode value and a somethingElse field.

Here's how you can use this discriminated union:

let foo: Foo;

// This is a valid Foo with a statusCode of '0000'
foo = { statusCode: '0000', something: 'hello' };

// This is a valid Foo with a statusCode of '1234'
foo = { statusCode: '1234', somethingElse: 'hello' };

// Invalid
const foo2: Foo = { statusCode: '0000', somethingElse: 'something' }
  • Related