[Typescript] The partial Inference Problem

Problem for partial inference:

export const makeSelectors = <
  TSource,
  TSelectors extends Record<string, (source: TSource) => any> = {},
>(
  selectors: TSelectors,
) => {
  return selectors;
};

interface Source {
  firstName: string;
  middleName: string;
  lastName: string;
}

const selectors = makeSelectors<Source>({
  getFullName: (source) =>
    `${source.firstName} ${source.middleName} ${source.lastName}`,
  getFirstAndLastName: (source) => `${source.firstName} ${source.lastName}`,
  getFirstNameLength: (source) => source.firstName.length,
});

So makeSelectorshas type: const makeSelectors: <Source, {}>(selectors: {}) => {}

 

If remove the generic type:

const selectors = makeSelectors({
  getFullName: (source) =>
    `${source.firstName} ${source.middleName} ${source.lastName}`,
  getFirstAndLastName: (source) => `${source.firstName} ${source.lastName}`,
  getFirstNameLength: (source) => source.firstName.length,
});

Then it has type: 

/*
const makeSelectors: <unknown, {
    getFullName: (source: unknown) => string;
    getFirstAndLastName: (source: unknown) => string;
    getFirstNameLength: (source: unknown) => any;
}>(selectors: {
    getFullName: (source: unknown) => string;
    getFirstAndLastName: (source: unknown) => string;
    getFirstNameLength: (source: unknown) => any;
}) => {
    getFullName: (source: unknown) => string;
    getFirstAndLastName: (source: unknown) => string;
    getFirstNameLength: (source: unknown) => any;
}
*/

 

So Problem is If we are using one generic type TSource in more than one place, like so:

export const makeSelectors = <
  TSource,
  TSelectors extends Record<string, (source: TSource) => any> = {},
>

Typescript only infer the first generic type, doesn't infer from the second.

 

Solution could be make it a high order function, each time give one generic type:

import { Equal, Expect } from '../helpers/type-utils';

export const makeSelectors =
  <TSource = 'expect to receive a source type'>() =>
  <TSelectors extends Record<string, (source: TSource) => any> = {}>(
    selectors: TSelectors
  ) => {
    return selectors;
  };

interface Source {
  firstName: string;
  middleName: string;
  lastName: string;
}

const selectors = makeSelectors<Source>()({
  getFullName: (source) =>
    `${source.firstName} ${source.middleName} ${source.lastName}`,
  getFirstAndLastName: (source) => `${source.firstName} ${source.lastName}`,
  getFirstNameLength: (source) => source.firstName.length,
});

type tests = [
  Expect<Equal<typeof selectors['getFullName'], (source: Source) => string>>,
  Expect<
    Equal<typeof selectors['getFirstAndLastName'], (source: Source) => string>
  >,
  Expect<
    Equal<typeof selectors['getFirstNameLength'], (source: Source) => number>
  >
];

 

posted @ 2023-02-27 14:58  Zhentiw  阅读(23)  评论(0编辑  收藏  举报