根据传入的模块类型获取不同模块存在问题。

huangapple go评论57阅读模式
英文:

A problem with getting different Modules based on the type of Module passed in

问题

我想传入一个不同类型的数组,以便我可以获得子模块的不同组合。

但我传入单一类型是可以的,当我传入多种类型时,它编译错误。

我该如何更改这个?

英文:

I want to pass in an array of different types so that I can get different combinations of submodules.

But I pass in a single type which is fine, and when I pass in multiple types, it compiles incorrectly.

How do I change this?

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.org playground

I want to pass in an array of different types so that I can get different combinations of submodules.

But I pass in a single type which is fine, and when I pass in multiple types, it compiles incorrectly.

How do I change this?

答案1

得分: 2

通过以下方式重写:

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;
// }

你可能会看到一个联合类型。你必须将它改为交叉类型以使其工作:

// 要么将这个包作为依赖项添加,要么复制粘贴所需的类型。不需要授信。
// 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`将`Core<Module>`解包为`CommonModule & VideoModule & ChatModule`
  // 这是为了可读性,生产环境中可以将其删除
  throw 0;
}
let client3 = createClient([Module.Chat, Module.Video]);
// let client3: CommonModule & VideoModule & ChatModule
英文:

By rewriting as

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

you may see a union there. You must change it to intersection to work

// 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 &#39;type-fest&#39;

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

答案2

得分: 1

感谢 @Dimava
我已经找到两种解决这个问题的方法。

1. 使用 type-fest 中的 UnionToIntersection by @Dimava

export type UnionToIntersection&lt;Union&gt; = (
    // `extends unknown` 始终成立,并用于将 `Union` 转换为分布式条件类型。
    Union extends unknown
        // 函数参数的联合会形成一个交集,因此将联合类型用作函数的唯一参数。
        ? (distributedUnion: Union) =&gt; void
        // 这不会发生。
        : never
        // 推断 `Intersection` 类型,因为 TypeScript 将函数的联合参数表示为该联合的交集。
) extends ((mergedIntersection: infer Intersection) =&gt; void)
    ? Intersection
    : never;

export enum Module {
  &#39;Video&#39; = 0,
  &#39;Chat&#39;,
}

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&lt;T extends Module&gt; = CommonModule &amp; UnionToIntersection&lt;ModulesMaps[T]&gt;

export function createClient&lt;T extends Module&gt;(modules: T[]): Core&lt;T&gt; &amp; 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(); //编译错误
client3.getChatModule(); //编译错误
client3.init();

2. 使用元组类型

export type ModuleSelect&lt;T extends readonly Module[], TModuleSelect extends Record&lt;Module, any&gt;&gt; =   | 
T extends [infer F extends Module, ...infer L extends readonly Module[]] ? TModuleSelect[F] &amp; ModuleSelect&lt;L, TModuleSelect&gt;   : unknown;

export enum Module {
  &#39;Video&#39; = 0,
  &#39;Chat&#39;,
}

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&lt;T extends Array&lt;Module&gt;&gt; = CommonModule &amp; ModuleSelect&lt;T, ModulesMaps&gt;;

export function createClient&lt;T extends Module[]&gt;(_modules: [...T]): Core&lt;T&gt; {
  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();
英文:

Thank you for @Dimava
I have found two ways to solve the problem.

1. Using UnionToIntersection in type-fest by @Dimava

export type UnionToIntersection&lt;Union&gt; = (
	// `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) =&gt; void
		// This won&#39;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) =&gt; void)
	? Intersection
	: never;

export enum Module {
  &#39;Video&#39; = 0,
  &#39;Chat&#39;,
}

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&lt;T extends Module&gt; = CommonModule &amp; UnionToIntersection&lt;ModulesMaps[T]&gt;

export function createClient&lt;T extends Module&gt;(modules: T[]): Core&lt;T&gt; &amp; 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. Use tuple types

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

export type ModuleSelect&lt;T extends readonly Module[], TModuleSelect extends Record&lt;Module, any&gt;&gt; =   | 
T extends [infer F extends Module, ...infer L extends readonly Module[]] ? TModuleSelect[F] &amp; ModuleSelect&lt;L, TModuleSelect&gt;   : unknown;

export enum Module {
  &#39;Video&#39; = 0,
  &#39;Chat&#39;,
}

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&lt;T extends Array&lt;Module&gt;&gt; = CommonModule &amp; ModuleSelect&lt;T, ModulesMaps&gt;;

export function createClient&lt;T extends Module[]&gt;(_modules: [...T]): Core&lt;T&gt; {
  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

huangapple
  • 本文由 发表于 2023年6月29日 11:46:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76577943.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定