Typescript discriminated unions

Created By: chatGPT

Discriminated unions in TypeScript allow us to define a type that can be one of several different types, usually distinguished by a common property that acts as a discriminator. This is especially useful when dealing with complex data structures where an entity can take on multiple shapes. Here's how you can implement it.
// Define the discriminated union type
interface Circle {
  kind: 'circle';
  radius: number;
}

interface Square {
  kind: 'square';
  sideLength: number;
}

type Shape = Circle | Square;
In this example, we have two interfaces: Circle and Square, each with a kind property that identifies which type it is. The union type Shape can either be a Circle or a Square.
function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.sideLength * shape.sideLength;
  }
}
The area function takes a Shape as an argument and uses a switch statement to determine the type of shape passed to it. This leverages the kind property to differentiate between shapes and calculate their area accordingly. Here is how you might use this in practice.
const myCircle: Circle = { kind: 'circle', radius: 10 };
const mySquare: Square = { kind: 'square', sideLength: 5 };

console.log(area(myCircle)); // Outputs: 314.159...
console.log(area(mySquare)); // Outputs: 25
This allows you to work more effectively with complex types and ensures type safety when dealing with different structures. If you try to call the area function with an incompatible shape, TypeScript will throw an error.
const invalidShape = { kind: 'triangle', base: 5, height: 10 }; // TypeScript Error
console.log(area(invalidShape)); // Error: Argument of type '{ kind: string; base: number; height: number; }' is not assignable to parameter of type 'Shape'.
By using discriminated unions, you can also enforce exhaustiveness checks by making sure all possible cases are handled in switch statements or other conditional logic. This can help catch bugs at compile time.
function getDescription(shape: Shape): string {
  switch (shape.kind) {
    case 'circle':
      return `Circle with radius ${shape.radius}`;
    case 'square':
      return `Square with side length ${shape.sideLength}`;
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck; // This will ensure all cases are handled
  }
}
Introduction And SetupVariablesData TypesAnyUnknownVoidNeverStringNumberBooleanArrayTupleEnumObjectInterfaceType AliasMethodsFunctionArrow FunctionReturn TypeParametersDefault ParametersOptional ParametersRest ParametersControl FlowIf StatementElse StatementSwitch StatementFor LoopWhile LoopDo While LoopFor...of LoopFor...in LoopBreak StatementContinue StatementFunctionsFunction OverloadingRecursive FunctionExpression FunctionOperatorsArithmetic OperatorsAssignment OperatorsComparison OperatorsLogical OperatorsBitwise OperatorsConditional (ternary) OperatorData StructuresArrayTupleObjectMapSetCommentsSingle Line CommentMulti Line CommentSyntaxType AnnotationsType InferenceNamespaceModuleExportImportDecoratorsAmbient DeclarationsEvent HandlingEvent ListenersAddEventListenerRemoveEventListenerError HandlingTry...catch StatementThrow StatementFinally BlockCustom Error TypesAsync AwaitPromisesGenericsConditional TypesMapped TypesIntersection TypesUnion TypesLiteral TypesDiscriminated UnionsType GuardsTypeof GuardInstanceof GuardAssertion FunctionsModule Augmentation