在泛型数组中强制执行子类型

Enforce sub-type in array of generics

我正在尝试创建一个列列表,其中每一列都有我希望由类型系统强制执行的约束。列渲染器有一个通用的 value 属性 并且还有一个 valueGetter 应该 return 适合自定义渲染器的类型。

我的困难是,当我声明数组时,我不知道每种类型是什么,所以我求助于 unknown,这使得我对列的分配不强制类型。

export interface Grid<R> {
    // How can I specify that the second type param should be inferred from its usage?
    columns: GridColumn<R, unknown>[];
}

我的问题是:如何在我的 GridColumn 接口

中推断出 value 的类型
/**
 * R is the type of the whole record for current row
 * V is the type of `ColumnRenderer#value`
 */
export interface GridColumn<R, V> {
    renderer: ColumnRendererSpec<R, V>;
}

/** The component that will be dynamically instantiated */
export interface ColumnRenderer<V> {
    value: V;
}

interface ColumRendererConstructor<V> {
     new(...args: any[]): ColumnRenderer<V>;
}

interface ValueGetter<R,V> {
    (record: R): V
}

export interface ColumnRendererSpec<R, V> {
    type: ColumRendererConstructor<V>;

    /** Retrieves what is going to be passed to set ColumnRenderer#value  */
    valueGetter: ValueGetter<R, V>;
}

export interface Grid<R> {
    columns: GridColumn<R, unknown>[];
}

我想这样称呼它

export interface Icon {
    code: string;
    label: string;
}

export interface Tooltip {
    text: string;
    tooltipText: string;
}

/** Type of record being passed to this grid */
interface MyRecord {
  code: string;
  label: string;
  text: string;
  description: string;
}

/** Renderers */
@Component({template: `<p><clr-icon icon="value.code"></clr-icon>value.label</p>`})
export class IconRenderer implements ColumnRenderer<Icon> {
    value: Icon;
}
@Component({template: `<p [title]="value.tooltipText</p>value.text`})
export class TooltipRenderer implements ColumnRenderer<Tooltip> {
    value: Tooltip;
}


const grid: Grid<MyRecord> = {
    columns: [
        {
            renderer: {
                type: IconRenderer,
                // This is compiling but shouldn't because valueGetter should return an Icon                    // 
                valueGetter: (r) => 2,
            },
        },
        {
            renderer: {
                type: TooltipRenderer,
                // This doesn't compile, but I wish I didn't have to cast
                valueGetter: (r) => 2,
            },
        } as ColumnRendererSpec<MyRecord, Tooltip>,    
    ]
};

正如我在代码中的评论所表明的,我想强制 valueGetter return 是从 ColumnRendererSpec#type 的值 属性[ 推断出的正确类型=20=]

可能不是最好的方法,但是,由于列渲染器实现了 interface ColumnRenderer<V>,并且由于 V 应该是 valueGetters 的 return 类型,我们可以通过列渲染器构造函数和 valueGetter 方法作为实用程序方法的参数,该方法将创建渲染器规范对象并帮助 getter 从传递给 [=15= 的泛型推断其 return 类型].

示例:

  function createColumnRendererSpec<R, V>(
     ctor: ColumnRendererConstructor<V>, 
     valueGetter: (r: R) => V): ColumnRendererSpec<R, V> {
         return {
             type: ctor,
             valueGetter
         }
     }