Typescript: Get the guarded type of a custom type guard

Created on 22 Mar 2019  Β·  2Comments  Β·  Source: microsoft/TypeScript

Search Terms

custom type guard, ReturnType, Parameters

Suggestion

Add a type combinator to extract the guarded type of custom type guard function and/or refine the type of a custom type guard to something stronger than (...) => boolean.
I don't think it's currently doable using either conditional type expressions or ambient combinators like ReturnType or Parameters.

Use Cases

Avoid carrying around the type parameter of a generic type.
My specific use case is when a complex generic type is encapsulated in a simpler generic type like the following:

type Message<
  RolesT extends Role,
  ProtocolIdentifierT extends ProtocolIdentifier,
  SourceSelectorT extends ProcessSelector<RolesT>,
  DestinationSelectorT extends ProcessSelector<RolesT>,
  DirectionT extends Direction,
  KindT extends Kind,
  PayloadT extends Payload
> = {
  uuid: Uuid;
  protocol: ProtocolIdentifierT;
  source: MatchingProcess<RolesT, SourceSelectorT>;
  destination: MatchingProcess<RolesT, DestinationSelectorT>;
  direction: DirectionT;
  kind: KindT;
  payload: PayloadT;
};

type Protocol<
  RolesT extends Role,
  ProtocolIdentifierT extends ProtocolIdentifier,
  MessageT extends Message<RolesT, ProtocolIdentifierT, any, any, any, any, any>
> = {
  roles: RolesT[];
  identifier: ProtocolIdentifier;
  isMessage: (m: AnyMessage) => m is MessageT;
};

Then if I want to define a Server class, right now I need to carry round a complex Message type already embedded in the simpler Protocol type:

class Server<
  MessageT extends AnyMessage, // need to carry around T
  ProtocolT extends Protocol< // and perform complex type combination
    (
      | keyof MessageT["source"]["roles"] // need to deep extract types just to correctly reconstruct ProtocolT
      | keyof MessageT["destination"]["roles"]) &
      Role,
    MessageT["protocol"],
    MessageT
  >
> {
  protocol: ProtocolT;
  constructor(protocol: ProtocolT) {
    this.protocol = protocol;
  }
  listen = (
    listener: (message: MessageT) => Promise<MessageT>,
  ):=> {
    ...
  };
}

With a combinator like GuardedType, this could be written as:

type MessageOf<ProtocolT extends Protocol<any, any, any>> = GuardedType<ProtocolT["isMessage"]>;
class Server<ProtocolT extends Protocol<any, any, any>> {
  ...
  listen = (listen: (message: MessageOf<ProtocolT>) => Promise<MessageOf<ProtocolT>>) => { ... }
}

Examples

In summary, if this combinator were named GuardedType, then this would be used like is:

const isFoo = (x : any): x is Foo => { ... }
type ExtractedFoo = GuardedType<typeof isFoo>;

Checklist

My suggestion meets these guidelines:

  • [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [x] This feature would agree with the rest of TypeScript's Design Goals.
Question

Most helpful comment

Does this work?

interface Foo {
    name: string;
}

declare const foo: Foo;

function isFoo(x: any): x is Foo {
    return x === foo;
}

type GuardedType<T> = T extends (x: any) => x is (infer T) ? T : never;
type ExtractedFoo = GuardedType<typeof isFoo>;

All 2 comments

Does this work?

interface Foo {
    name: string;
}

declare const foo: Foo;

function isFoo(x: any): x is Foo {
    return x === foo;
}

type GuardedType<T> = T extends (x: any) => x is (infer T) ? T : never;
type ExtractedFoo = GuardedType<typeof isFoo>;

This works, thanks!
Maybe adding this to the list of predefined combinators, or improve documentation on infer could be useful though?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  Β·  3Comments

Antony-Jones picture Antony-Jones  Β·  3Comments

DanielRosenwasser picture DanielRosenwasser  Β·  3Comments

Roam-Cooper picture Roam-Cooper  Β·  3Comments

jbondc picture jbondc  Β·  3Comments