Item 74: Know How to Reconstruct Types at Runtime
要点
- 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.
正文
ts
interface CreateComment {
postId: string
title: string
body: string
}
ts
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')
})
ts
const val = { postId: '123', title: 'First', body: 'That is all' }
type ValType = typeof val
// ^? type ValType = { postId: string; title: string; body: string; }
ts
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')
}
})
ts
// api.ts
export interface CreateComment {
postId: string
title: string
body: string
}
ts
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')
})