typescript

TypeScript - Define type alias as subtype of another type alias?


In TypeScript, is it possible for a type alias to "implement" (or become subtype of) another type alias?

For example, I have the following type aliases (TS Playground Link):

type Predicate<TValue> = {
    op: "EQUAL" | "NOT_EQUAL",
    value: TValue,
}

type Predicates = Record<string, Predicate<string> | Predicate<boolean>>

/**
 * An implementation of the {@link Predicates} type.
 */
type AnimalPredicates = {
    species: Predicate<string>,
    isMammal: Predicate<boolean>,
}

// EXAMPLE:
const animalPredicates: AnimalPredicates = {
    species: { op: "EQUAL", value: "shiba_inu" },
    isMammal: { op: "EQUAL", value: true },
}

// `AnimalPredicates` is a subtype of `Predicates` so this is allowed
const animalPredicates2: Predicates = animalPredicates

The AnimalPredicates type alias is an implementation of the Predicates type (i.e. AnimalPredicates is subtype of Predicates).

Although I added a JSDoc comment saying that AnimalPredicates implements Predicates, it does not prevent me from doing something like:

type AnimalPredicates = {
  // Predicate<number> is NOT allowed as record value in `Predicates`
  // But TS compiler does not complain...
  species: Predicate<number>,  
}

Is there a workaround to let the TS compiler know that AnimalPredicates must be a subtype of the Predicate type?


Solution

  • If you are looking for the type-level satisfies, it looks like this:

    type Satisfies<I, T extends I> = T
    
    type Predicates = Record<string, Predicate<string> | Predicate<boolean>>
    
    type AnimalPredicates = Satisfies<Predicates, {
        species: Predicate<string>,
        isMammal: Predicate<boolean>,
    }>
    
    type NotPredicates = Satisfies<Predicates, {
        species: Predicate<string>,
        isMammal: string,
    //     Type '{ species: Predicate<string>; isMammal: string; }' does not satisfy the constraint 'Predicates'.
    //       Property 'isMammal' is incompatible with index signature.
    //         Type 'string' is not assignable to type 'Predicate<string> | Predicate<boolean>'.(2344)
    }>