Skip to content

Latest commit

 

History

History
121 lines (95 loc) · 5.33 KB

runtime-types.md

File metadata and controls

121 lines (95 loc) · 5.33 KB

Item 74: Know How to Reconstruct Types at Runtime

Things to Remember

  • TypeScript types are erased before your code is run. You can't access them at runtime without additional tooling.
  • Know your options for runtime types: using a distinct runtime type system (such as Zod), generating TypeScript types from values (json-schema-to-typescript), and generating values from your TypeScript types (typescript-json-schema).
  • If you have another specification for your types (e.g., a schema), use that as the source of truth.
  • If you need to reference external TypeScript types, use typescript-json-schema or an equivalent.
  • Otherwise, weigh whether you prefer another build step or another system for specifying types.

Code Samples

interface CreateComment {
  postId: string;
  title: string;
  body: string;
}

💻 playground


app.post('/comment', (request, response) => {
  const {body} = request;
  if (
    !body ||
    typeof body !== 'object' ||
    Object.keys(body).length !== 3 ||
    !('postId' in body) || typeof body.postId !== 'string' ||
    !('title' in body) || typeof body.title !== 'string' ||
    !('body' in body) || typeof body.body !== 'string'
  ) {
    return response.status(400).send('Invalid request');
  }
  const comment = body as CreateComment;
  // ... application validation and logic ...
  return response.status(200).send('ok');
});

💻 playground


const val = { postId: '123', title: 'First', body: 'That is all'};
type ValType = typeof val;
//   ^? type ValType = { postId: string; title: string; body: string; }

💻 playground


import { z } from 'zod';

// runtime value for type validation
const createCommentSchema = z.object({
  postId: z.string(),
  title: z.string(),
  body: z.string(),
});

// static type
type CreateComment = z.infer<typeof createCommentSchema>;
//   ^? type CreateComment = { postId: string; title: string; body: string; }

app.post('/comment', (request, response) => {
  const {body} = request;
  try {
    const comment = createCommentSchema.parse(body);
    //    ^? const comment: { postId: string; title: string; body: string; }
    // ... application validation and logic ...
    return response.status(200).send('ok');
  } catch (e) {
    return response.status(400).send('Invalid request');
  }
});

💻 playground


// api.ts
export interface CreateComment {
  postId: string;
  title: string;
  body: string;
}

💻 playground


import Ajv from 'ajv';

import apiSchema from './api.schema.json';
import {CreateComment} from './api';

const ajv = new Ajv();

app.post('/comment', (request, response) => {
  const {body} = request;
  if (!ajv.validate(apiSchema.definitions.CreateComment, body)) {
    return response.status(400).send('Invalid request');
  }
  const comment = body as CreateComment;
  // ... application validation and logic ...
  return response.status(200).send('ok');
});

💻 playground