typescript 根据传入的模块类型获取不同模块的问题

m528fe3b  于 2023-06-30  发布在  TypeScript
关注(0)|答案(2)|浏览(122)

我想传入一个不同类型的数组,这样我就可以得到子模块的不同组合。
但是我传入了一个单一的类型,这很好,当我传入多个类型时,它编译不正确。我该如何更改此设置?

export enum Module {
  'Video' = 0,
  'Chat',
}

export interface ModulesMaps {
  [Module.Video]: VideoModule;
  [Module.Chat]: ChatModule;
}

export interface VideoModule {
  getVideoModule():  string;
}

export interface ChatModule {
  getChatModule():  string;
}

export interface CommonModule {
  init(): void;
}

export type Core<T extends Module> = CommonModule & ModulesMaps[T]

export function createClient<T extends Module>(modules: T[]): Core<T>{
  // fake code
  return undefined as unknown as Core<T>;
}

let client1 = createClient([Module.Video]);
client1.getVideoModule();
client1.init();

let client2 = createClient([Module.Chat]);
client2.getChatModule();
client2.init();

let client3 = createClient([ Module.Chat | Module.Video  ]);
client3.getVideoModule(); //compile error
client3.getChatModule(); //compile error
client3.init();

Playground:typescriptlang.orgPlayground
我想传入一个不同类型的数组,这样我就可以得到子模块的不同组合。
但是我传入了一个单一的类型,这很好,当我传入多个类型时,它编译不正确。我该如何更改此设置?

irlmq6kh

irlmq6kh1#

通过重写为

export type Core<T extends Module> = CommonModule & { foo: ModulesMaps[T] }
export function createClient<T extends Module>(modules: T[]): Core<T> & unknown {
  throw 0;
}
let client3 = createClient([Module.Chat, Module.Video]);
// let client3: CommonModule & {
//     foo: VideoModule | ChatModule;
// }

你可以在那里看到一个联盟。你必须把它改成交集才能工作

// Either add this package as a dependency or copy-paste the needed types. No credit required.
// https://github.com/sindresorhus/type-fest/blob/main/source/union-to-intersection.d.ts
import { UnionToIntersection } from 'type-fest'

export type Core<T extends Module> = CommonModule & UnionToIntersection<ModulesMaps[T]>
export function createClient<T extends Module>(modules: T[]): Core<T> & unknown {
  // `& unknown` unwraps `Core<Module>` into `CommonModule & VideoModule & ChatModule`
  // this is for understandability, feel free to remove it for production
  throw 0;
}
let client3 = createClient([Module.Chat, Module.Video]);
// let client3: CommonModule & VideoModule & ChatModule
7vhp5slm

7vhp5slm2#

谢谢@Dimava,我找到了两种解决问题的方法。

1.@Dimava在type-fest中使用UnionToIntersection

export type UnionToIntersection<Union> = (
    // `extends unknown` is always going to be the case and is used to convert the
    // `Union` into a [distributive conditional
    // type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
    Union extends unknown
        // The union type is used as the only argument to a function since the union
        // of function arguments is an intersection.
        ? (distributedUnion: Union) => void
        // This won't happen.
        : never
        // Infer the `Intersection` type since TypeScript represents the positional
        // arguments of unions of functions as an intersection of the union.
) extends ((mergedIntersection: infer Intersection) => void)
    ? Intersection
    : never;

export enum Module {
  'Video' = 0,
  'Chat',
}

export interface ModulesMaps {
  [Module.Video]: VideoModule;
  [Module.Chat]: ChatModule;
}

export interface VideoModule {
  getVideoModule():  string;
}

export interface ChatModule {
  getChatModule():  string;
}

export interface CommonModule {
  init(): void;
}

export type Core<T extends Module> = CommonModule & UnionToIntersection<ModulesMaps[T]>

export function createClient<T extends Module>(modules: T[]): Core<T> & unknown{
  throw 0;
}

let client1 = createClient([Module.Video]);
client1.getVideoModule();
client1.init();

let client2 = createClient([Module.Chat]);
client2.getChatModule();
client2.init();

let client3 = createClient([ Module.Chat, Module.Video  ]);
client3.getVideoModule(); //compile error
client3.getChatModule(); //compile error
client3.init();

Playground

2.使用元组类型

export type ModuleSelect<T extends Array<Module>, TModuleSelect extends Record<Module, any>> = (0 extends keyof T
  ? TModuleSelect[T[0]]
  : Record<string, never>) &
  (1 extends keyof T ? TModuleSelect[T[1]] : Record<string, never>) &
  (2 extends keyof T ? TModuleSelect[T[2]] : Record<string, never>) &
  (3 extends keyof T ? TModuleSelect[T[3]] : Record<string, never>) &
  (4 extends keyof T ? TModuleSelect[T[4]] : Record<string, never>) &
  (5 extends keyof T ? TModuleSelect[T[5]] : Record<string, never>) &
  (6 extends keyof T ? TModuleSelect[T[6]] : Record<string, never>) &
  (7 extends keyof T ? TModuleSelect[T[7]] : Record<string, never>) &
  (8 extends keyof T ? TModuleSelect[T[8]] : Record<string, never>) &
  (9 extends keyof T ? TModuleSelect[T[9]] : Record<string, never>) &
  (10 extends keyof T ? TModuleSelect[T[10]] : Record<string, never>);

export enum Module {
  'Video' = 0,
  'Chat',
}

export interface ModulesMaps {
  [Module.Video]: VideoModule;
  [Module.Chat]: ChatModule;
}

export interface VideoModule {
  getVideoModule(): string;
}

export interface ChatModule {
  getChatModule(): string;
}

export interface CommonModule {
  init(): void;
}

export type Core<T extends Array<Module>> = CommonModule & ModuleSelect<T, ModulesMaps>;

export function createClient<T extends Module[]>(_modules: [...T]): Core<T> {
  throw 0;
}

const client1 = createClient([Module.Video]);
client1.getVideoModule();
client1.init();

const client2 = createClient([Module.Chat]);
client2.getChatModule();
client2.init();

const client3 = createClient([Module.Chat, Module.Video]);
client3.getVideoModule();
client3.getChatModule();
client3.init();

Playground

相关问题