【问题标题】:TypeScript infer optional generic typeTypeScript 推断可选的泛型类型
【发布时间】:2021-01-27 16:40:50
【问题描述】:

我已经超出了我的深度。我想对具有可选“解析”函数的泛型进行推断,该函数反过来返回格式化值[或抛出]。

代码说了一千多个单词,所以就这样吧:

export type RouteDefInput<TInput = unknown> = {
  parse: (input: unknown) => TInput;
};
export type RouteDefResolver<
  TContext = unknown,
  TInput = unknown,
  TOutput = unknown
> = (opts: { ctx: TContext; input: TInput }) => Promise<TOutput> | TOutput;

export type RouteDef<
  TContext = unknown,
  TInput = unknown,
  TOutput = unknown
> = {
  input?: RouteDefInput<TInput>;
  resolve: RouteDefResolver<TContext, TInput, TOutput>;
};

export type inferRouteInput<
  TRoute extends RouteDef<any, any, any>
> = TRoute['input'] extends RouteDefInput<any>
  ? ReturnType<TRoute['input']['parse']>
  : undefined;

const context = {};
function createRoute<TInput, TOutput>(
  route: RouteDef<typeof context, TInput, TOutput>,
) {
  return route;
}

function stringParser(input: unknown): string {
  if (typeof input === 'string') {
    return input;
  }
  throw new Error('not a string');
}

const myRoute1 = createRoute({
  input: {
    parse: stringParser,
  },
  resolve({ input }) {
    return {
      output: input,
    };
  },
});

const myRoute2 = createRoute({
  resolve({ input }) {
    return {
      output: input,
    };
  },
});

// this should render MyRouteInput1 as "string"
type MyRouteInput1 = inferRouteInput<typeof myRoute1>;

// this should render MyRouteInput2 as "undefined" (works)
type MyRouteInput2 = inferRouteInput<typeof myRoute2>;

我怎样才能正确地使inferRouteInput 在这两种情况下都适用?

我已经为此苦恼太久了 - 如果你想玩,这里是游乐场链接:https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBDAnmYcBKECuNgBFgBmAkgHZjYA8AKqefALxyYkDWJEA7iQHxyMDeAKDhwwAQygBnYAC44ACgCWZbHOZtOJAJR9eNFTADcggL7HQkWAmSoM2PITTBJEADYA3YFArC4VAMIQJDggDEys7FwANL76dHzhGtGxAPLY8YzqkSSCvIzyEGAwknL8cADGMCByAUEhRnDKdDW02HAmOvS8AApQEAC2itLUaTB0vAA+fqN0xoIW0PBIKOhYOPgEPiK1waBhWZox2637EYep6W2ZZ1y5CUIiTdgA-HJ264Qn1CfcxiJQzjcnjeawcBCcLg8XmogV2oSifhOCKoM2wv1McwWVmWqGUBC872AX1ihLge2AJAAJpJVvYNhQxCREAjGcy4KzuHdGFRCQBtADkTxg-IAumTQhTqbSPsQDAymZyRM90MAYJgoCQqDZqHzBQZRQLxFJgKLFXA1FTCMpgJS5uUgpJ4Pa4WF+GZBARmJVFEEKgCxDhCd8DMjUTBuPJfH17CC6YQKDiIAQKnU9sikdNLuGYjoHnAAWqNfnQcYTIIPV6YD6SHBHVBlABzboSaRQJQGC1JbRyOuNuB5xTJ+SJ5NCvj0Rj83skBv83O+f6q9U1oV-dq+GAACz6HDgJGAu4AolA+m3+ex4GJazB6zO56Xy87HXB+ohCQBGBLlf2B0HyAcdv2C6iC2sjXreTagVARztDBAKQp4-6NAY7TziIi6FjWeboXAazNMhdAwSI7rETEHR2g68CvoSABMX4-sAhL-lGgJQkhY4dEBOEFsuXE4bhWZyEKRHtGuJhkVocwAPRSQgm5DLWm5YK4lL5pKXhwAAsm+oInJ+Yg0gARNODaGYIOJaTp9h6QkeIErpcoji+Vk4O+6KCDJckKZISmYCpamWlAlmEicdEGXAhnMJSVr7pShkKBw0AsJIWjmTYwUOXQdGMHZUAhY5NhJs5tHokAA

谢谢一百万! ????

任何链接到关于这个主题或 TypeScript 泛型的良好阅读也很受欢迎,因为我很难找到好的阅读来源。

【问题讨论】:

    标签: typescript generics


    【解决方案1】:

    我得到了这个工作。解决方案有点暴力——重载createRoute 函数。这是我所拥有的:

    export type RouteDefInput<TInput = unknown> = {
      parse: (input: unknown) => TInput;
    };
    export type RouteDefResolver<
      TContext = unknown,
      TInput = unknown,
      TOutput = unknown
    > = (opts: { ctx: TContext; input: TInput }) => Promise<TOutput> | TOutput;
    
    // export type RouteDef<
    //   TContext = unknown,
    //   TInput = unknown,
    //   TOutput = unknown
    // > = {
    //   input?: RouteDefInput<TInput>;
    //   resolve: RouteDefResolver<TContext, TInput, TOutput>;
    // };
    
    export type RouteDefWithInput<
      TContext = unknown,
      TInput = unknown,
      TOutput = unknown
    > = {
      input: RouteDefInput<TInput>;
      resolve: RouteDefResolver<TContext, TInput, TOutput>;
    };
    
    export type RouteDefWithoutInput<
      TContext = unknown,
      TInput = unknown,
      TOutput = unknown
    > = {
      resolve: RouteDefResolver<TContext, TInput, TOutput>;
    };
    export type RouteDef<TContext = unknown, TInput = unknown, TOutput = unknown> =
      | RouteDefWithInput<TContext, TInput, TOutput>
      | RouteDefWithoutInput<TContext, TInput, TOutput>;
    
    export type inferRouteInput<
      TRoute extends RouteDef<any, any, any>
    > = TRoute extends RouteDef<any, infer Input, any> ? Input : never;
    
    const context = {};
    function createRoute<TInput, TOutput>(
      route: RouteDefWithInput<typeof context, TInput, TOutput>,
    ): RouteDefWithInput<typeof context, TInput, TOutput>;
    function createRoute<TInput, TOutput>(
      route: RouteDefWithoutInput<typeof context, TInput, TOutput>,
    ): RouteDefWithoutInput<typeof context, TInput, TOutput>;
    function createRoute(route: any) {
      return route;
    }
    
    function stringParser(input: unknown): string {
      if (typeof input === 'string') {
        return input;
      }
      throw new Error('not a string');
    }
    
    const myRoute1 = createRoute({
      input: {
        parse: stringParser,
      },
      resolve(input) {
        return {
          output: input,
        };
      },
    });
    
    const myRoute2 = createRoute({
      resolve({ input }) {
        return {
          output: input,
        };
      },
    });
    
    // this should render MyRouteInput1 as "string"
    type MyRouteInput1 = inferRouteInput<typeof myRoute1>;
    
    // this should render MyRouteInput2 as "unknown" (works)
    type MyRouteInput2 = inferRouteInput<typeof myRoute2>;
    
    

    【讨论】:

    • 这打破了对resolve({ input })的推断
    【解决方案2】:

    此代码有效:

    export type RouteDefInput<TInput = unknown> = {
      parse: (input: unknown) => TInput;
    };
    export type RouteDefResolver<
      TContext = unknown,
      TInput = unknown,
      TOutput = unknown
    > = (opts: { ctx: TContext; input: TInput }) => Promise<TOutput> | TOutput;
    
    export type RouteDef<
      TContext = unknown,
      TInput = unknown,
      TOutput = unknown
    > = {
      input?: RouteDefInput<TInput>;
      resolve: RouteDefResolver<TContext, TInput, TOutput>;
    };
    
    export type inferRouteInput<
      TRoute extends RouteDef<any, any, any>
    > = TRoute extends RouteDef<any, infer TInput, any>
      ? NonNullable<TInput>
      : undefined;
    
    const context = {};
    function createRoute<TInput, TOutput>(
      route: RouteDef<typeof context, TInput, TOutput>,
    ) {
      return route;
    }
    
    function stringParser(input: unknown): string {
      if (typeof input === 'string') {
        return input;
      }
      throw new Error('not a string');
    }
    
    const myRoute1 = createRoute({
      input: {
        parse: stringParser,
      },
      resolve({ input }) {
        return {
          output: input,
        };
      },
    });
    
    const myRoute2 = createRoute({
      resolve({ input }) {
        return {
          output: input,
        };
      },
    });
    
    // this should render MyRouteInput1 as "string"
    type MyRouteInput1 = inferRouteInput<typeof myRoute1>;
    
    // this should render MyRouteInput2 as "undefined" (works)
    type MyRouteInput2 = inferRouteInput<typeof myRoute2>;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-17
      • 2017-10-04
      • 1970-01-01
      • 2016-12-05
      • 2018-01-18
      • 2021-07-28
      • 2021-10-18
      • 2020-07-23
      相关资源
      最近更新 更多