Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions table/src/-private/-type-tests/generic-wrapper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expectTypeOf } from 'expect-type';

import { headlessTable } from '../../index.ts';

import type { CellContext, ColumnConfig } from '../../index.ts';
import type { ComponentLike } from '@glint/template';

interface Person {
name: string;
}
declare const people: Person[];

// A shared table component takes a column list from its caller and builds the
// table itself. Its own cell args are a type parameter, so there is no Cell to
// read them from.
function makeTable<T, CellArgs>(
columnConfigList: ColumnConfig<T, unknown, unknown, CellArgs>[],
data: T[],
) {
return headlessTable(
{},
{ columns: () => columnConfigList, data: () => data },
);
}

declare const columnList: ColumnConfig<
Person,
unknown,
unknown,
{ groupBy: 'day' | 'week' }
>[];

const table = makeTable(columnList, people);

expectTypeOf(table.columns[0]!.Cell).toEqualTypeOf<
| ComponentLike<
CellContext<Person, unknown, unknown> & { groupBy: 'day' | 'week' }
>
| undefined
>();
12 changes: 12 additions & 0 deletions table/src/-private/interfaces/column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Constructor } from '../private-types';
import type { ComponentLike, ContentValue } from '@glint/template';

declare const rowType: unique symbol;
declare const cellArgsType: unique symbol;

/**
* What `value`, `options`, and a `Cell` receive.
Expand All @@ -26,6 +27,8 @@ type ColumnPluginOption<P = Plugin> = P extends BasePlugin
? [Constructor<P>, () => ColumnOptionsFor<SignatureFrom<P>>]
: [P | Constructor<P>, () => unknown];

export type { cellArgsType };

export type CellOptions = {
/**
* when no value is present for a given set of data for the given column config
Expand Down Expand Up @@ -113,6 +116,15 @@ export interface ColumnConfig<
* gives `headlessTable` a row type that depends on the order TypeScript checks the program in.
*/
readonly [rowType]?: T;

/**
* Type-only, never set.
*
* A column list written in place is a tuple, and the args come from its Cells.
* A list with a declared type is a plain array, which has no Cells to read,
* so the args it declares are read from here.
*/
readonly [cellArgsType]?: CellArgs;
}

export type ColumnKey<T> = NonNullable<ColumnConfig<T>['key']>;
22 changes: 17 additions & 5 deletions table/src/-private/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
UnwrapNamedArgs,
} from '@glint/template/-private/integration';
import type { ComponentSignatureArgs } from '@glint/template/-private/signature';
import type { cellArgsType } from './interfaces/column.ts';

/**
* The metas of columns that set one.
Expand Down Expand Up @@ -94,11 +95,22 @@ type UnionToIntersection<U> = (
* [{ Cell: GroupByCell }, { Cell: UpdateCell }]
* → { groupBy: ... } & { onUpdate: ... }
*/
export type CellArgsOf<Columns extends readonly unknown[]> = [
ProvidedCells<Columns>,
] extends [never]
? unknown
: UnionToIntersection<ExtraArgsOf<ProvidedCells<Columns>>>;
export type CellArgsOf<Columns extends readonly unknown[]> =
number extends Columns['length']
? DeclaredCellArgs<Columns[number]>
: [ProvidedCells<Columns>] extends [never]
? unknown
: UnionToIntersection<ExtraArgsOf<ProvidedCells<Columns>>>;

/**
* The cell args a column config declares, for a list that is a plain array.
* Such a list has no Cells to read the args from, the way a tuple does.
*/
type DeclaredCellArgs<Column> = Column extends {
[cellArgsType]?: infer CellArgs;
}
? CellArgs
: unknown;

/**
* The `@options` the Cells of a table ask for, from their args.
Expand Down