Skip to content

Commit

Permalink
refactor: name duplication detection types (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
grumd authored Apr 23, 2024
1 parent 7f50c8a commit 6ee1d62
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 17 deletions.
23 changes: 6 additions & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,16 @@ type InferState<Configs> = Configs extends [
} & InferState<Rest>
: unknown;

type IsDuplicated<Name, Names extends unknown[]> = Names extends [
infer One,
...infer Rest,
]
? One extends Name
? true
: IsDuplicated<Name, Rest>
: false;

type HasDuplicatedNames<
Configs,
Names extends string[] = [],
> = Configs extends [
SliceConfig<infer Name, infer _Value, infer Actions>,
...infer Rest,
]
? Name extends Names[number]
? true
: IsDuplicated<keyof Actions, Names> extends true
? true
: HasDuplicatedNames<Rest, [Name, ...Names]>
? Extract<Name | keyof Actions, Names[number]> extends never
? HasDuplicatedNames<Rest, [Name, ...Names]>
: true
: false;

type HasDuplicatedArgs<Configs, State> = Configs extends [
Expand Down Expand Up @@ -78,9 +67,9 @@ export function createSlice<
return config;
}

// FIXME no any in a reasonable way?
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function withSlices<Configs extends SliceConfig<string, unknown, any>[]>(
export function withSlices<
Configs extends SliceConfig<string, unknown, NonNullable<unknown>>[],
>(
...configs: Configs
): IsValidConfigs<Configs> extends true
? (
Expand Down
38 changes: 38 additions & 0 deletions tests/02_type.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,44 @@ test('slice type: single slice', () => {
>(true);
});

test('slice type: withSlices', () => {
const countSlice = createSlice({
name: 'count',
value: 0,
actions: {
inc: () => (prev) => prev + 1,
reset: () => () => 0,
},
});

const textSlice = createSlice({
name: 'text',
value: 'Hello',
actions: {
updateText: (newText: string) => () => newText,
reset: () => () => 'Hello',
},
});

type CountTextState = {
count: number;
inc: () => void;
text: string;
updateText: (newText: string) => void;
reset: () => void;
};

const slices = withSlices(countSlice, textSlice);

expectType<
(
set: (fn: (prevState: CountTextState) => Partial<CountTextState>) => void,
) => CountTextState
>(slices);

expectType<TypeEqual<typeof slices, never>>(false);
});

test('name collisions: same slice names', () => {
const countSlice = createSlice({
name: 'count',
Expand Down

0 comments on commit 6ee1d62

Please sign in to comment.