带有 Symbol.observable ponyfill 的 TypeScript 可观察对象

TypeScript observable object with Symbol.observable ponyfill

我正在尝试键入一个可观察的对象并使用自定义 Symbol.observable ponyfill(类似于下面显示的 system-observable ponyfill; 代码),但我想不出办法去做。

我试过使用 typeof $$observable 作为密钥,并尝试将 Symbol.observable 变成 unique symbol,但这些都不起作用。我不得不求助于转换对象,但这种方法的问题是我必须转换整个对象(下面未显示),这使得捕获其他类型错误变得更加困难,因为对象中的其他属性正在输入错误。我也不能只做 ReturnType<typeof makeObservableContainer> 因为我想在一个单独的接口中定义它而不必依赖于函数。

或者我想知道是否有一种方法可以将其投射到比投射整个对象更细粒度的级别(如果事实证明不投射是不可能的)。

我提供了一个游乐场 link。任何帮助将不胜感激。

第一次尝试(没有铸造)

错误信息

Property '[Symbol.observable]' is missing in type '{ [x: string]: () => { [x: string]: ((observer: Observer) => { unsubscribe(): void; }) | (() => { [x: string]: ((observer: Observer) => { unsubscribe(): void; }) | ...; subscribe(observer: Observer): { ...; }; }); subscribe(observer: Observer): { ...; }; }; }' but required in type 'Container'.(2741)

代码

// system-observable.ts
declare global {
  interface SymbolConstructor {
    readonly observable: symbol
  }
}

const $$observable = /* #__PURE__ */ (() =>
  (typeof Symbol === 'function' && Symbol.observable) || '@@observable')()

export default $$observable

// observer-creator.ts
interface Observer {
    next(): void;
}

interface Observable {
    subscribe: (observer: Observer) => { unsubscribe(): void };
    [Symbol.observable](): Observable;
}

interface Container {
    // How do I type this?
    [Symbol.observable](): Observable;
}

function makeObservableContainer(): Container {
    // error here because this is not assignable to `Container`
    return {
        [$$observable]() {
            return {
                subscribe(observer: Observer) {
                    // TODO
                    return {
                        unsubscribe() {
                            // TODO
                        }
                    }
                },
                [$$observable]() { return this }
            }
        },

        // other properties that should conform to `Container` interface
    }
}

游乐场Link:Provided


第二次尝试(独特的符号和一点铸造)

错误

Type '() => { subscribe(observer: Observer): { unsubscribe(): void; }; observable: Observable; }' is not assignable to type '() => Observable'. Property '[Symbol.observable]' is missing in type '{ subscribe(observer: Observer): { unsubscribe(): void; }; observable: Observable; }' but required in type 'Observable'.(2322)

代码

// system-observable.ts
declare global {
  interface SymbolConstructor {
    readonly observable: unique symbol
  }
}

const $$observable: SymbolConstructor['observable'] = /* #__PURE__ */ (() =>
  (typeof Symbol === 'function' && Symbol.observable) || '@@observable')() as SymbolConstructor['observable']

export default $$observable

// observer-creator.ts
interface Observer {
    next(): void;
}

interface Observable {
    subscribe: (observer: Observer) => { unsubscribe(): void };
    [Symbol.observable](): Observable;
}

interface Container {
    // How do I type this?
    [Symbol.observable](): Observable;
}

function makeObservableContainer(): Container {
    // error here because this is not assignable to `Container`
    return {
        [$$observable]() {
            return {
                subscribe(observer: Observer) {
                    // TODO
                    return {
                        unsubscribe() {
                            // TODO
                        }
                    }
                },
                [$$observable]() { return this }
            }
        },

        // other properties that should conform to `Container` interface
    }
}

游乐场Link:Provided

如果将 "@@observable" 字符串转换为 any,您可以键入 $$observable 作为唯一符号,这样您就可以将其用作接口的键:

const $$observable: unique symbol = /* #__PURE__ */ (() =>
    (typeof Symbol === 'function' && Symbol.observable) || ('@@observable' as any))();

export default $$observable

// observer-creator.ts
interface Observer {
    next(): void;
}

interface Observable {
    subscribe: (observer: Observer) => { unsubscribe(): void };
    [$$observable](): Observable;
}

interface Container {
    [$$observable](): Observable;
}