0

Try to get the ApiResponse<T> static TS generic type from the generic runtime type created by io-ts. No information in official documentation

Expect type:

// runtime type
const ApiResponseCodec = <C extends t.Mixed>(codec: C) =>
  t.type({
    code: t.string,
    message: t.union([t.string, t.undefined]),
    result: codec,
  });

// expect type
type ApiResponse<T> = {
  code: string;
  message: string | undefined;
  result: T;
}

Complete code:

import { pipe } from 'fp-ts/lib/function';
import * as E from 'fp-ts/lib/Either';
import * as t from 'io-ts';

const ArticleDTOCodec = t.type({
  id: t.number,
  title: t.string,
});
type ArticleDTO = t.TypeOf<typeof ArticleDTOCodec>;

const PaginationResultCodec = <C extends t.Mixed>(codec: C) =>
  t.type({
    resultList: codec,
    totalItem: t.number,
  });

type PaginationResult<C extends t.Mixed> = t.TypeOf<ReturnType<typeof PaginationResultCodec<C>>>;

const ApiResponseCodec = <C extends t.Mixed>(codec: C) =>
  t.type({
    code: t.string,
    message: t.union([t.string, t.undefined]),
    result: codec,
  });

// Does not infer the type correctly.
type ApiResponse<C extends t.Mixed> = t.TypeOf<ReturnType<typeof ApiResponseCodec<C>>>;

const GetArticlesByPageResponseCodec = ApiResponseCodec(PaginationResultCodec(t.array(ArticleDTOCodec)));

export const decodeApiResponse = (res: ApiResponse<PaginationResult<ArticleDTO[]>>) => {
  return pipe(
    res,
    GetArticlesByPageResponseCodec.decode,
    E.fold(
      (e) => 'no',
      (res) => 'yes',
    ),
  );
};

Got error throws by ApiResponse<PaginationResult<ArticleDTO[]>>:

Type '{ resultList: unknown; totalItem: number; }' does not satisfy the constraint 'Mixed'.
  Type '{ resultList: unknown; totalItem: number; }' is missing the following properties from type 'Type<any, any, unknown>': name, is, validate, encode, and 7 more.ts(2344)

TS Playground

Lin Du
  • 88,126
  • 95
  • 281
  • 483

1 Answers1

0

You'd need to pass a t.Type<ArticleDTO[]> instead of ArticleDTO[] in the ApiResponse<PaginationResult<ArticleDTO[]>> and also wrap the PaginationResult<...> in the t.Type. Instead, you could use the actual type decoded by the codec as the type variable. Something like this.

import { pipe } from 'fp-ts/lib/function';
import * as E from 'fp-ts/lib/Either';
import * as t from 'io-ts';

const ArticleDTOCodec = t.type({
  id: t.number,
  title: t.string,
});
type ArticleDTO = t.TypeOf<typeof ArticleDTOCodec>;

const PaginationResultCodec = <A>(codec: t.Type<A>) =>
  t.type({
    resultList: codec,
    totalItem: t.number,
  });

type PaginationResult<A> = t.TypeOf<ReturnType<typeof PaginationResultCodec<A>>>;

const ApiResponseCodec = <A>(codec: t.Type<A>) =>
  t.type({
    code: t.string,
    message: t.union([t.string, t.undefined]),
    result: codec,
  });

type ApiResponse<A> = t.TypeOf<ReturnType<typeof ApiResponseCodec<A>>>;

const GetArticlesByPageResponseCodec = ApiResponseCodec(PaginationResultCodec(t.array(ArticleDTOCodec)));

export const decodeApiResponse = (res: ApiResponse<PaginationResult<ArticleDTO[]>>) => {
  return pipe(
    res,
    GetArticlesByPageResponseCodec.decode,
    E.fold(
      (e) => 'no',
      (res) => 'yes',
    ),
  );
};
sukovanej
  • 658
  • 2
  • 8
  • 18