可调用的打字稿界面不可以调用吗?

时间:2019-05-01 19:58:21

标签: typescript

我正在尝试编写ts-optchain的固定版本。该功能将尝试返回根对象的副本,其中包含更改后的拼接。这样就不会以任何方式更改或修改原件。但是,对于尚未修改的对象区域,它们将复制到浅复制操作中作为引用(通过Object.assign(...))。

我要验证的测试如下:

const example = { a: { b: { c: { d: 5 } } } };
const out = osc(example).a.b.c.d(6);
expect(out).to.be.deep.eq({ a: { b: { c: { d: 6 } } } });

...其中osc(可选的设置链)是我为模仿opt-chain的{​​{1}}函数而创建的函数。

我希望结果类似于oc

以上方法很难编写,阅读和维护。因此,发挥这一功能的理由。

实施

我这样做的尝试如下:

Object.assign({}, example, {a: Object.assign({}, example.a, {b: Object.assign({}, example.a.b, {c: Object.assign({}, example.a.b.c, {d: 6})})})});

问题

不幸的是,我得到了错误:// ----- Types ----- // Generic type "R" -> The returned root object type when setting a value // Generic type "T" -> The type for the proxy object interface TSOSCDataSetter<R, T> { (value: Readonly<T>): Readonly<R>; } type TSOSCObjectWrapper<R, T> = { [K in keyof T]-?: TSOSCType<R, T[K]> }; interface TSOSCArrayWrapper<R, T> { length: TSOSCType<R, number>; [K: number]: TSOSCType<R, T>; } interface TSOSCAny<R> extends TSOSCDataSetter<R, any> { [K: string]: TSOSCAny<R>; // Enable deep traversal of arbitrary props } type TSOSCDataWrapper<R, T> = 0 extends (1 & T) // Is T any? (https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360) ? TSOSCAny<R> : T extends any[] // Is T array-like? ? TSOSCArrayWrapper<R, T[number]> : T extends object // Is T object-like? ? TSOSCObjectWrapper<R, T> : TSOSCDataSetter<R, T>; export type TSOSCType<R, T> = TSOSCDataSetter<R, T> & TSOSCDataWrapper<R, T>; // ----- Helper functions ----- function setter<K extends keyof V, V>(original: () => (Readonly<V> | undefined), key: K, value: Readonly<V[K]>): Readonly<V> { // Shallow copies this layer with the spliced in value specified. Works with both dictionaries and lists. return Object.assign(typeof key === "string" ? {} : [], original(), { [key]: value }); } function getter<K extends keyof V, V>(object: Readonly<V> | undefined, key: K): Readonly<V[K]> | undefined { // Assists in optionally fetching down a continuous recursive chain of index-able objects (dictionaries & lists) return object === undefined ? object : object[key]; } // ----- Internal recursive optional set chain function ----- function _osc<R, K extends keyof V, V>(root: Readonly<R> | undefined, get_chain: () => (Readonly<V> | undefined), set_chain: (v: Readonly<V>) => Readonly<R>): TSOSCType<R, V> { // `root` is passed in as an argument and never used. This is just to maintain the typing for <R>. // `get_chain` is a constructed recursive function that will return what the value of this object is at this node. // `set_chain` is a constructed recursive function that will assist in building and splicing in the specified value. return new Proxy( {} as TSOSCType<R, V>, // Blank object. I don't use `target`. { get: function (target, key: K): TSOSCType<R, V[K]> { const new_get_chain = (): (Readonly<V[K]> | undefined) => getter(get_chain(), key); const new_set_chain = (v: Readonly<V[K]>): Readonly<R> => set_chain(setter(get_chain, key, v)); return _osc(root, new_get_chain, new_set_chain); }, apply: function (target, thisArg, args: [Readonly<V>]): Readonly<R> { return set_chain(args[0]); } } ); } // ----- Exposed optional set chain function ----- export function osc<R, K extends keyof R>(root: Readonly<R> | undefined): TSOSCType<R, R> { const set_chain = (value: Readonly<R>): Readonly<R> => value; return _osc(root, () => root, set_chain); } 。这就是我开始困惑的地方。从osc(...).a.b.c.d is not a function(和osc)函数返回的是类型_osc,它扩展了接口TSOSCType。接口TSOSCDataSetter指定继承接口的对象本身是可调用的:

TSOSCDataSetter

interface TSOSCDataSetter<R, T> { (value: Readonly<T>): Readonly<R>; } osc都重新调谐的是_osc,类型为Proxy(与TSOSCType一样)。该代理对象有助于构建链并键入完整的对象链。但对于这个问题更重要的是,实现ts-optchain方法:

apply

那为什么apply: function (target, thisArg, args: [Readonly<V>]): Readonly<R> { return set_chain(args[0]); } 类型不能被调用?

1 个答案:

答案 0 :(得分:3)

发生这种情况的原因是,仅可以调用函数周围的代理(并设置apply陷阱)。强制转换{} as TSOSCType<R, V>掩盖了您正在做的事情在运行时是不可能的事实,并指示TypeScript(错误地)信任您。

将该语句更改为function(){} as unknown as TSOSCType<R, V>使其可以正常工作。

See this in the Playground

作为一般经验法则,只要在使用TypeScript时遇到运行时TypeError,就意味着TypeScript信任您并且您出卖了它。那几乎总是意味着演员。当您遇到此类错误时,您的演员表应该是直接的犯罪嫌疑人。