json-schema-to-ts
json-schema-to-ts copied to clipboard
Types not resolving correctly after 2.9.2
Hi,
first of all I want to say thank you for this great library!
I use it to share typed api routes between backend and frontend. Basically I define a contract using json schemas. This is then used for the validation and for the types on the backend and for creating the queries for the frontend.
Unfortunately upgrading to 2.12.0 or 3.0.0 breaks my code. I heavily simplified the example below to show the issue. Some types seem to be not needed but this is due to the simplification.
If I define the schema externally for initContract
then this works with the newer versions as well.
But my inline schema for the params
property only works with version 2.9.2.
Any idea what causes this behaviour? I would like to continue using inline schema definitions.
Types with 2.9.2
Types with 2.12.0
Simplified example
import type { FromSchema, JSONSchema } from 'json-schema-to-ts'
// Simplified version
type ContractRoute = {
params: JSONSchema
query: JSONSchema
}
type Contract = {
[key: string]: ContractRoute
}
type ProcessContract<T extends Contract> = {
[K in keyof T]: T[K]
}
const initContract = <TRouter extends Contract>(routes: ProcessContract<TRouter>): TRouter => routes
type ContractRouteImplementation<T extends ContractRoute> = () => {
handler: (request: { Params: FromSchema<T['params']>; Query: FromSchema<T['query']> }) => void
}
type ServerObj<T extends Contract> = {
[TKey in keyof T]: ContractRouteImplementation<T[TKey]>
}
type InitialisedServer<T extends Contract> = {
contract: T
routes: ServerObj<T>
}
const initServer = <T extends Contract>(contract: T, routes: ServerObj<T>): InitialisedServer<T> => ({
contract,
routes,
})
// Example
const externSchema = {
type: 'object',
properties: {
str: { type: 'string' },
},
required: ['str'],
additionalProperties: false,
} as const
const contract = initContract({
demo: {
params: {
type: 'object',
properties: {
str: { type: 'string' },
},
required: ['str'],
additionalProperties: false,
} as const,
query: externSchema,
},
})
initServer(contract, {
demo: () => ({
handler: async (request) => {},
}),
})
Hi @hamanuha and thanks for reaching out!
This seems to be related to https://github.com/ThomasAribart/json-schema-to-ts/issues/165, you can look at https://github.com/ThomasAribart/json-schema-to-ts/blob/main/documentation/FAQs/applying-from-schema-on-generics.md
This worked for me:
type ContractRouteImplementation<
CONTRACT_ROUTE extends ContractRoute,
PARAMS = FromSchema<CONTRACT_ROUTE["params"]>,
QUERY = FromSchema<CONTRACT_ROUTE["query"]>,
> = () => {
handler: (request: { Params: PARAMS; Query: QUERY }) => void;
};
Hi @ThomasAribart,
thanks for the hint. Unfortunately this does not work for me. I am using typescript 5.3.3. Maybe it's related to some config values?
Would you mind sharing your tsconfig?
Edit:
When using your asConst function it works with both versions of the ContractRouteImplementation
.
But only if I use as const
as well.
const contract = initContract({
demo: {
params: asConst({
type: 'object',
properties: {
str: { type: 'string' },
},
required: ['str'],
additionalProperties: false,
} as const),
query: externSchema,
},
})
Edit2:
When using the Narrow
type from your package inside FromSchema
it works also for the inline schema without having to use asConst
type ParamsFromSchema<T extends ContractRoute, TParams = Narrow<T['params']>> = TParams extends JSONSchema
? FromSchema<TParams>
: never
Hey @ThomasAribart,
did you see my second edit? Can I help with something to resolve this issue?