【问题标题】:Typescript array of different generic types不同泛型类型的打字稿数组
【发布时间】:2018-08-13 14:42:39
【问题描述】:

如果我有一个数组a,其中每个元素都是一个由两个属性firstsecond 组成的对象,我应该如何声明`a 的类型以便始终满足以下条件?

 ForAll(x in a)(type(x.first) == T iff type(x.second) == (T => string))

例如,我想确保 a[3].second(a[3].first) 是类型安全的。

【问题讨论】:

  • var a = [];就是你要找的。​​span>
  • @ShahriyarMammadli 谢谢你,但事实并非如此。例如,我可以写a.push({b: "w"}, x => x.c),它会通过编译但在运行时失败

标签: typescript


【解决方案1】:

数组的元素类型需要是“存在类型”,我们可以用伪代码写成exists T. { first: T, second: (arg: T) => string }TypeScript currently does not support existential types natively.

一种可能的解决方法是使用闭包对存在类型进行编码,如this answer 中所述。如果您不想在运行时使用真正的闭包,您可以使用一个实用程序库,它为存在类型提供类型定义(基于使用索引访问类型的类型函数的编码)和函数来生成和使用执行的存在类型类型转换,但只是运行时的身份:

// Library
// (Based in part on https://bitbucket.org/espalier-spreadsheet/espalier/src/b9fef3fd739d42cacd479e50f20cb4ab7078d534/src/lib/type-funcs.ts?at=master&fileviewer=file-view-default#type-funcs.ts-23
// with inspiration from https://github.com/gcanti/fp-ts/blob/master/HKT.md)

const INVARIANT_MARKER = Symbol();
type Invariant<T> = {
    [INVARIANT_MARKER](t: T): T
};

interface TypeFuncs<C, X> {}

const FUN_MARKER = Symbol();
type Fun<K extends keyof TypeFuncs<{}, {}>, C> = Invariant<[typeof FUN_MARKER, K, C]>;

const BAD_APP_MARKER = Symbol();
type BadApp<F, X> = Invariant<[typeof BAD_APP_MARKER, F, X]>;
type App<F, X> = [F] extends [Fun<infer K, infer C>] ? TypeFuncs<C, X>[K] : BadApp<F, X>;

const EX_MARKER = Symbol();
type Ex<F> = Invariant<[typeof EX_MARKER, F]>;
function makeEx<F, X>(val: App<F, X>): Ex<F> { 
    return <any>val;
}
function enterEx<F, R>(exVal: Ex<F>, cb: <X>(val: App<F, X>) => R): R { 
    return cb(<any>exVal);
}

// Use case

const F_FirstAndSecond = Symbol();
type F_FirstAndSecond = Fun<typeof F_FirstAndSecond, never>;
interface TypeFuncs<C, X> { 
    [F_FirstAndSecond]: { first: X, second: (arg: X) => string };
}

let myArray: Ex<F_FirstAndSecond>[];
myArray.push(makeEx<F_FirstAndSecond, number>({ first: 42, second: (x) => x.toString(10) }));
myArray.push(makeEx<F_FirstAndSecond, {x: string}>({ first: {x: "hi"}, second: (x) => x.x }));
for (let el of myArray) { 
    enterEx(el, (val) => console.log(val.second(val.first)));
}

(如果有足够的兴趣,我可能会适当地发布这个库......)

【讨论】:

【解决方案2】:

如果您想要一个数组,其中 first 始终具有相同的类型,则可以这样做

interface IArrayGeneric<T> {
    first: T;
    second: (arg: T) => string;
}

const a: Array<IArrayGeneric<   type   >>;

这将确保您不能将任何不满足上述要求的对象放入a,但也会将T 限制为您选择的一个特定type

【讨论】:

  • 不,IArrayGeneric&lt;any&gt; 将接受 firstsecond 不匹配的对象。例如,a.push({ first: 42, second: (x: {y: string}) =&gt; x.y}) 编译。
  • 你是完全正确的。这看起来是一个相当常见的用例,我很惊讶没有一个相当简单的解决方案。
猜你喜欢
  • 2021-11-26
  • 1970-01-01
  • 2020-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-22
  • 2018-05-02
  • 2022-01-04
相关资源
最近更新 更多