如果我有一个数组a
,其中每个元素是一个由两个属性first
和second
组成的对象,那么我应该如何声明`a's类型,以便始终满足以下条件?
ForAll(x in a)(type(x.first) == T iff type(x.second) == (T => string))
例如,我想确保a[3].second(a[3].first)
是类型安全的。
答案 0 :(得分:2)
数组的元素类型需要为“现有类型”,我们可以用伪代码编写为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)));
}
(如果有足够的兴趣,我可以适当地发布此库...)
答案 1 :(得分:0)
如果要让第一个数组始终具有相同类型的数组,则可以这样做
interface IArrayGeneric<T> {
first: T;
second: (arg: T) => string;
}
const a: Array<IArrayGeneric< type >>;
这将确保您不能将任何不满足上述要求的对象放入a
,但也会将T
限制为您选择的特定type
。 / p>