Skip to content

Commit 308038b

Browse files
committed
feat: add UnitDetail and fix types
1 parent 67236c1 commit 308038b

File tree

14 files changed

+135
-397
lines changed

14 files changed

+135
-397
lines changed

action.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Denops } from "@denops/std";
22

33
import type { Promish } from "./_typeutil.ts";
4-
import type { Detail, IdItem } from "./item.ts";
4+
import type { Detail, IdItem, UnitDetail } from "./item.ts";
55

66
/**
77
* Parameters for invoking an action.
@@ -28,7 +28,7 @@ export type InvokeParams<T extends Detail> = {
2828
/**
2929
* An action that can be invoked from the picker.
3030
*/
31-
export type Action<T extends Detail> = {
31+
export type Action<T extends Detail = UnitDetail> = {
3232
/**
3333
* Invoke the action.
3434
*

action_test.ts

Lines changed: 13 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,24 @@
11
import { assertType, type IsExact } from "@std/testing/types";
22
import { DenopsStub } from "@denops/test/stub";
3-
import type { Promish } from "./_typeutil.ts";
3+
import type { Detail, UnitDetail } from "./item.ts";
44
import type { Action, InvokeParams } from "./action.ts";
55

66
Deno.test("Action", async (t) => {
77
const denops = new DenopsStub();
8-
const action: Action<{ a: string }> = {
9-
invoke: () => {},
10-
};
118

12-
await t.step("passed type is equal to the type restriction", () => {
13-
const items = action.invoke(
14-
denops,
15-
{} as InvokeParams<{ a: string }>,
16-
{},
17-
);
18-
assertType<
19-
IsExact<
20-
typeof items,
21-
Promish<void | true>
22-
>
23-
>(true);
9+
await t.step("without type constraint is equal to UnitDetail", () => {
10+
assertType<IsExact<Action, Action<UnitDetail>>>(true);
2411
});
2512

26-
await t.step("passed type establishes the type restriction", () => {
27-
const items = action.invoke(
28-
denops,
29-
{} as InvokeParams<{ a: string; b: string }>,
30-
{},
31-
);
32-
assertType<
33-
IsExact<
34-
typeof items,
35-
Promish<void | true>
36-
>
37-
>(true);
13+
await t.step("invoke follows the type constraint", () => {
14+
const action: Action<{ a: string }> = { invoke: () => {} };
15+
action.invoke(denops, {} as InvokeParams<{ a: string }>, {});
16+
action.invoke(denops, {} as InvokeParams<{ a: string; b: string }>, {});
17+
// @ts-expect-error: 'a' is missing
18+
action.invoke(denops, {} as InvokeParams<{ b: string }>, {});
19+
// @ts-expect-error: 'a' is missing
20+
action.invoke(denops, {} as InvokeParams<Detail>, {});
21+
// @ts-expect-error: 'a' is missing
22+
action.invoke(denops, {} as InvokeParams<UnitDetail>, {});
3823
});
39-
40-
await t.step("passed type does not establish the type restriction", () => {
41-
const items = action.invoke(
42-
denops,
43-
// @ts-expect-error: 'a' is missing
44-
{} as InvokeParams<{ b: string }>,
45-
{},
46-
);
47-
assertType<
48-
IsExact<
49-
typeof items,
50-
Promish<void | true>
51-
>
52-
>(true);
53-
});
54-
55-
await t.step(
56-
"check if the type constraint correctly triggers the type checking",
57-
() => {
58-
const action1: Action<{ a: string }> = {
59-
invoke: () => {},
60-
};
61-
const action2: Action<{ b: string }> = {
62-
invoke: () => {},
63-
};
64-
const action3: Action<{ c: string }> = {
65-
invoke: () => {},
66-
};
67-
function strictFunction<T extends { a: string }>(_: Action<T>) {}
68-
strictFunction(action1);
69-
// @ts-expect-error: 'a' is missing
70-
strictFunction(action2);
71-
// @ts-expect-error: 'a' is missing
72-
strictFunction(action3);
73-
},
74-
);
7524
});

curator_test.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
import { assertType, type IsExact } from "@std/testing/types";
22
import { DenopsStub } from "@denops/test/stub";
33
import type { IdItem } from "./item.ts";
4-
import type { CurateParams, Curator } from "./curator.ts";
4+
import type { Curator } from "./curator.ts";
55

66
Deno.test("Curator", () => {
77
const denops = new DenopsStub();
88
const curator: Curator<{ a: string }> = {
99
curate: async function* () {},
1010
};
11-
const items = curator.curate(
12-
denops,
13-
{} as CurateParams,
14-
{},
15-
);
11+
const items = curator.curate(denops, { args: [], query: "" }, {});
1612
assertType<
1713
IsExact<
1814
typeof items,

item.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
export type Detail = Record<PropertyKey, unknown>;
55

66
/**
7-
* An empty detail attribute.
7+
* An unit of Detail.
88
*/
9-
export type EmptyDetail = Record<never, never>;
9+
export type UnitDetail = Record<never, never>;
1010

1111
/**
1212
* An item processed by the picker.

item_test.ts

Lines changed: 54 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,60 @@
11
import { assertType, type IsExact } from "@std/testing/types";
2-
import type { Detail, EmptyDetail, IdItem } from "./item.ts";
2+
import type { Detail, UnitDetail } from "./item.ts";
33

4-
Deno.test("IdItem", async (t) => {
5-
await t.step("id is 'unknown'", () => {
6-
const item: IdItem<EmptyDetail> = {
7-
id: "unknown",
8-
value: "",
9-
detail: {},
10-
};
11-
assertType<IsExact<typeof item, IdItem<EmptyDetail>>>(true);
4+
Deno.test("Detail", async (t) => {
5+
await t.step("is usable as a type constraint", () => {
6+
function constraint<T extends Detail>(_: T) {}
7+
constraint({});
8+
constraint({ a: "" });
9+
constraint({ a: "", b: "" });
10+
// @ts-expect-error: string is not assignable
11+
constraint("");
12+
// @ts-expect-error: number is not assignable
13+
constraint(0);
14+
// @ts-expect-error: boolean is not assignable
15+
constraint(true);
16+
// @ts-expect-error: array is not assignable
17+
constraint([]);
18+
// @ts-expect-error: Date is not assignable
19+
constraint(new Date());
20+
// @ts-expect-error: function is not assignable
21+
constraint(() => {});
1222
});
1323

14-
await t.step("detail must be 'Detail'", () => {
15-
const base = { id: "unknown", value: "" };
16-
const _items: IdItem<Detail>[] = [
17-
{
18-
...base,
19-
// @ts-expect-error: number is not Detail
20-
detail: 1,
21-
},
22-
{
23-
...base,
24-
// @ts-expect-error: string is not Detail
25-
detail: "",
26-
},
27-
{
28-
...base,
29-
// @ts-expect-error: boolean is not Detail
30-
detail: true,
31-
},
32-
{
33-
...base,
34-
// @ts-expect-error: array is not Detail
35-
detail: [],
36-
},
37-
{
38-
...base,
39-
// @ts-expect-error: Date is not Detail
40-
detail: new Date(),
41-
},
42-
{
43-
...base,
44-
detail: {},
45-
},
46-
{
47-
...base,
48-
detail: {
49-
a: "string",
50-
b: 1,
51-
c: true,
52-
d: [],
53-
e: new Date(),
54-
f: () => {},
55-
0: "number",
56-
[Symbol("g")]: "symbol",
57-
},
58-
},
59-
];
24+
await t.step("is NOT an unit of Detail", () => {
25+
assertType<IsExact<Detail & { a: string }, { a: string }>>(false);
26+
// misc
27+
assertType<IsExact<Detail, Detail>>(true);
28+
assertType<IsExact<Detail & Detail, Detail>>(true);
29+
assertType<IsExact<Detail & Detail, Detail>>(true);
30+
});
31+
});
32+
33+
Deno.test("UnitDetail", async (t) => {
34+
await t.step("is NOT usable as a type constraint", () => {
35+
function constraint<T extends UnitDetail>(_: T) {}
36+
constraint({});
37+
constraint({ a: "" });
38+
constraint({ a: "", b: "" });
39+
// string is assignable!
40+
constraint("");
41+
// number is assignable!
42+
constraint(0);
43+
// boolean is assignable!
44+
constraint(true);
45+
// array is assignable!
46+
constraint([]);
47+
// Date is assignable!
48+
constraint(new Date());
49+
// function is assignable!
50+
constraint(() => {});
51+
});
52+
53+
await t.step("is an unit of Detail", () => {
54+
assertType<IsExact<UnitDetail & { a: string }, { a: string }>>(true);
55+
// misc
56+
assertType<IsExact<UnitDetail, UnitDetail>>(true);
57+
assertType<IsExact<UnitDetail & UnitDetail, UnitDetail>>(true);
58+
assertType<IsExact<UnitDetail & Detail, Detail>>(true);
6059
});
6160
});

matcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Denops } from "@denops/std";
22

3-
import type { Detail, IdItem } from "./item.ts";
3+
import type { Detail, IdItem, UnitDetail } from "./item.ts";
44

55
/**
66
* Parameters for matching items.
@@ -19,7 +19,7 @@ export type MatchParams<T extends Detail> = {
1919
/**
2020
* Matcher that filters items based on user input.
2121
*/
22-
export type Matcher<T extends Detail> = {
22+
export type Matcher<T extends Detail = UnitDetail> = {
2323
__phantom?: (_: T) => void; // This is required for type constraint.
2424

2525
/**

matcher_test.ts

Lines changed: 13 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,24 @@
11
import { assertType, type IsExact } from "@std/testing/types";
22
import { DenopsStub } from "@denops/test/stub";
3-
import type { IdItem } from "./item.ts";
3+
import type { UnitDetail } from "./item.ts";
44
import type { Matcher, MatchParams } from "./matcher.ts";
55

66
Deno.test("Matcher", async (t) => {
77
const denops = new DenopsStub();
8-
const matcher: Matcher<{ a: string }> = {
9-
match: async function* () {},
10-
};
118

12-
await t.step("passed type is equal to the type restriction", () => {
13-
const items = matcher.match(
14-
denops,
15-
{} as MatchParams<{ a: string }>,
16-
{},
17-
);
18-
assertType<
19-
IsExact<
20-
typeof items,
21-
AsyncIterableIterator<IdItem<{ a: string }>>
22-
>
23-
>(true);
9+
await t.step("without type constraint is equal to UnitDetail", () => {
10+
assertType<IsExact<Matcher, Matcher<UnitDetail>>>(true);
2411
});
2512

26-
await t.step("passed type establishes the type restriction", () => {
27-
const items = matcher.match(
28-
denops,
29-
{} as MatchParams<{ a: string; b: string }>,
30-
{},
31-
);
32-
assertType<
33-
IsExact<
34-
typeof items,
35-
AsyncIterableIterator<IdItem<{ a: string; b: string }>>
36-
>
37-
>(true);
13+
await t.step("match follows the type constraint", () => {
14+
const matcher: Matcher<{ a: string }> = { match: async function* () {} };
15+
matcher.match(denops, {} as MatchParams<{ a: string }>, {});
16+
matcher.match(denops, {} as MatchParams<{ a: string; b: string }>, {});
17+
// @ts-expect-error: 'a' is missing
18+
matcher.match(denops, {} as MatchParams<{ b: string }>, {});
19+
// @ts-expect-error: 'a' is missing
20+
matcher.match(denops, {} as MatchParams<Detail>, {});
21+
// @ts-expect-error: 'a' is missing
22+
matcher.match(denops, {} as MatchParams<UnitDetail>, {});
3823
});
39-
40-
await t.step("passed type does not establish the type restriction", () => {
41-
const items = matcher.match(
42-
denops,
43-
// @ts-expect-error: 'a' is missing
44-
{} as MatchParams<{ b: string }>,
45-
{},
46-
);
47-
assertType<
48-
IsExact<
49-
typeof items,
50-
AsyncIterableIterator<IdItem<{ a: string }>>
51-
>
52-
>(true);
53-
});
54-
55-
await t.step(
56-
"check if the type constraint correctly triggers the type checking",
57-
() => {
58-
const matcher1: Matcher<{ a: string }> = {
59-
match: async function* () {},
60-
};
61-
const matcher2: Matcher<{ b: string }> = {
62-
match: async function* () {},
63-
};
64-
const matcher3: Matcher<{ c: string }> = {
65-
match: async function* () {},
66-
};
67-
function strictFunction<T extends { a: string }>(_: Matcher<T>) {}
68-
strictFunction(matcher1);
69-
// @ts-expect-error: 'a' is missing
70-
strictFunction(matcher2);
71-
// @ts-expect-error: 'a' is missing
72-
strictFunction(matcher3);
73-
},
74-
);
7524
});

previewer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Denops } from "@denops/std";
22
import type { Promish } from "./_typeutil.ts";
3-
import type { Detail, IdItem, PreviewItem } from "./item.ts";
3+
import type { Detail, IdItem, PreviewItem, UnitDetail } from "./item.ts";
44

55
/**
66
* Parameters for previewing an item.
@@ -15,7 +15,7 @@ export type PreviewParams<T extends Detail> = {
1515
/**
1616
* Previewer that generates a preview for an item.
1717
*/
18-
export type Previewer<T extends Detail> = {
18+
export type Previewer<T extends Detail = UnitDetail> = {
1919
/**
2020
* Generates a preview for the specified item.
2121
*

0 commit comments

Comments
 (0)