@mantlebee/ts-data
    Preparing search index...

    2.2.0 — the DataSource class, to be extended

    Version 2.2.0 exports the DataSource class again, for inheritance: a source with members of its own extends it instead of implementing IDataSource from scratch. The release is additive: no existing export changes, and createDataSource stays the way to build a source.


    2.0.0 stopped exporting the class because a class cannot type its members from a generic record: new DataSource(reader, { filtering }) exposes no key in its type, where createDataSource(reader, { filtering }) infers every one. That still holds, and the factory is still the way to build a source. What the factory cannot do is give a source members of its own (a readAdults(), an overridden read()): that takes the class, and reimplementing the read cycle ("the latest read wins", the two phases, the events) behind IDataSource to get it is not an option. The class is exported to be extended.

    A subclass knows its extensions, so it types the keys the class cannot: one declare readonly per key given to super.

    class UsersDataSource extends DataSource<User> {
    declare public readonly filtering: FilterExtension<User>;
    declare public readonly pagination: PaginationExtension<User>;

    public constructor() {
    super(readUsers, {
    filtering: new FilterExtension<User>(),
    pagination: new PaginationExtension<User>(countUsers, 20),
    });
    }

    public async readAdults(): Promise<void> {
    this.filtering.setFilters([adult]);
    await this.read();
    }
    }

    useTable(new UsersDataSource()); // fits IDataSourceWith<User, "filtering" | "pagination">
    • declare is required. The keyed properties are own properties set by the constructor of the class. declare emits nothing; a plain field (public readonly filtering!: FilterExtension<User>) is redefined to undefined after super returns, under useDefineForClassFields, the default from the ES2022 target on. Nothing warns: the key is not a member of the base class, so the compiler sees no conflict.
    • The declarations are not checked against the record given to super: the subclass keeps the two in line.
    • Protected members: the pipeline (_extensions, every extension given, in the order of the keys), the reader (_reader) and the _eventBus of Subscribable. The read cycle itself stays private: override read() or dispose() around super.
    • Keys: a method or an accessor of the subclass is a member of the source, so it is refused as a key like items or read.

    A subclass adding behaviour to any source knows no key. Give it a factory of its own, typed like createDataSource:

    class CountingDataSource<TItem> extends DataSource<TItem> {
    public readsCount = 0;

    public override async read(): Promise<void> {
    this.readsCount++;
    await super.read();
    }
    }

    function createCountingDataSource<
    TItem,
    TExt extends DataSourceExtensionsDefinition<TItem>,
    >(reader: DataSourceReader<TItem>, extensions: TExt) {
    return new CountingDataSource<TItem>(
    reader,
    extensions,
    ) as CountingDataSource<TItem> & DataSourceExtensionProperties<TExt>;
    }

    const users = createCountingDataSource(readUsers, { filtering, sorting });
    users.sorting; // SortExtension<User>
    users.readsCount; // number

    New, from the package root: DataSource. Nothing else changes.

    npm install @mantlebee/ts-data@^2.2.0
    

    Nothing to migrate. Keep building the sources with createDataSource; extend DataSource where a source needs members of its own.