Skip to content

Commit e018fc7

Browse files
committed
More accurate types for EntityRefHierarchy
1 parent 938ced3 commit e018fc7

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed

src/EntityRefHierarchy.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {AnyRef} from "./AnyRef";
22

3-
export class EntityRefHierarchy<T extends Array<AnyRef | undefined>> {
3+
export class EntityRefHierarchy<T extends ReadonlyArray<AnyRef | undefined>> {
44
constructor(readonly list: T) {
55
if (this.list.filter(x => x).length < 1) {
66
throw new TypeError('EntityRefHierarchy must contain at least 1 ref');
@@ -10,7 +10,7 @@ export class EntityRefHierarchy<T extends Array<AnyRef | undefined>> {
1010
}
1111

1212
getRef<TType extends NonNullable<T[number]>['type']>(type: TType) {
13-
return this.list.find(ref => ref && ref.type === type) as (T[number] & { type: TType }) | undefined;
13+
return this.list.find(ref => ref && ref.type === type) as GetRefType<T, TType>
1414
}
1515

1616
get root() {
@@ -40,3 +40,15 @@ export class EntityRefHierarchy<T extends Array<AnyRef | undefined>> {
4040
}
4141
}
4242

43+
44+
type GetRefType<
45+
T extends readonly (AnyRef | undefined)[],
46+
TType extends string
47+
> = {
48+
[K in keyof T]:
49+
// If the slot is exactly `undefined` then just keep it.
50+
[T[K]] extends [undefined]
51+
? T[K]
52+
// Otherwise, if the non-undefined part has the right type, return the entire T[K] (which may be a union with undefined)
53+
: (NonNullable<T[K]> extends { type: TType } ? T[K] : never)
54+
}[number];

test/EntityRefHierarchyTest.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {createFactory} from "@src/createFactory";
22
import {EntityRefHierarchy} from "@src/EntityRefHierarchy";
33

4+
import {assert, IsExact} from "conditional-type-checks";
5+
46
describe('EntityRefHierarchy', () => {
57

68
const factoryWorkspace = createFactory('workspace', (id: number) => id);
@@ -22,7 +24,7 @@ describe('EntityRefHierarchy', () => {
2224

2325

2426
describe('simple hierarchy', () => {
25-
const hierarchy = new EntityRefHierarchy([REF_WORKSPACE, REF_PROJECT, REF_COLLECTION]);
27+
const hierarchy = new EntityRefHierarchy([REF_WORKSPACE, REF_PROJECT, REF_COLLECTION] as const);
2628

2729
it('getting root and leaf refs', () => {
2830
expect(hierarchy.root)
@@ -40,6 +42,17 @@ describe('EntityRefHierarchy', () => {
4042
.toEqual(REF_COLLECTION);
4143
});
4244

45+
46+
it('types test', () => {
47+
const workspaceRef = hierarchy.getRef('workspace');
48+
const projectRef = hierarchy.getRef('project');
49+
const collectionRef = hierarchy.getRef('collection');
50+
51+
assert<IsExact<typeof workspaceRef, ReturnType<typeof factoryWorkspace>>>(true);
52+
assert<IsExact<typeof projectRef, ReturnType<typeof factoryProject>>>(true);
53+
assert<IsExact<typeof collectionRef, ReturnType<typeof factoryCollection>>>(true);
54+
});
55+
4356
it('iterating', () => {
4457
expect([...hierarchy])
4558
.toEqual([REF_WORKSPACE, REF_PROJECT, REF_COLLECTION]);
@@ -51,7 +64,7 @@ describe('EntityRefHierarchy', () => {
5164
undefined as ReturnType<typeof factoryWorkspace> | undefined,
5265
REF_PROJECT,
5366
REF_COLLECTION
54-
]);
67+
] as const);
5568

5669
it('getting root and leaf refs', () => {
5770
expect(hierarchy.root)
@@ -69,6 +82,16 @@ describe('EntityRefHierarchy', () => {
6982
.toEqual(REF_COLLECTION);
7083
});
7184

85+
it('types test', () => {
86+
const workspaceRef = hierarchy.getRef('workspace');
87+
const projectRef = hierarchy.getRef('project');
88+
const collectionRef = hierarchy.getRef('collection');
89+
90+
assert<IsExact<typeof workspaceRef, ReturnType<typeof factoryWorkspace> | undefined>>(true);
91+
assert<IsExact<typeof projectRef, ReturnType<typeof factoryProject>>>(true);
92+
assert<IsExact<typeof collectionRef, ReturnType<typeof factoryCollection>>>(true);
93+
});
94+
7295
it('iterating', () => {
7396
expect([...hierarchy])
7497
.toEqual([REF_PROJECT, REF_COLLECTION]);

0 commit comments

Comments
 (0)