Skip to content

Commit

Permalink
Add type-tests to ensure consistent public API expectations (#37)
Browse files Browse the repository at this point in the history
* expect-type

* This is probably a decent start

* fix build

* Use stricter type check
  • Loading branch information
NullVoxPopuli authored Dec 6, 2024
1 parent 4061983 commit c38a015
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 3 deletions.
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc && vite build",
"build": "vite build",
"dev": "vite",
"prepare": "npm run build",
"lint": "prettier --check .",
"watch:types": "tsc --noEmit --watch",
"lint": "concurrently 'npm:lint:*(!fix)' --names 'lint:' --prefixColors=auto",
"lint:types": "tsc --noEmit",
"lint:prettier": "prettier --check .",
"lint:fix": "prettier --write .",
"test": "vitest"
},
"devDependencies": {
"@types/node": "^20.11.25",
"@vitest/browser": "^1.5.3",
"concurrently": "^9.0.1",
"expect-type": "^1.0.0",
"prettier": "^3.2.5",
"release-plan": "^0.9.0",
"typescript": "latest",
Expand Down
45 changes: 45 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 95 additions & 0 deletions src/public-api-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* The purpose of these tests is to make sure the types are exposed how we expect,
* and that we double-check what we're exposing as public API
*/
import {expectTypeOf} from 'expect-type';
import {Signal} from './wrapper.ts';

/**
* Top-Level
*/
expectTypeOf<keyof typeof Signal>().toEqualTypeOf<
'State' | 'Computed' | 'subtle' | 'isState' | 'isComputed' | 'isWatcher'
>();

/**
* Construction works as expected
*/
expectTypeOf(Signal.State<number>).toBeConstructibleWith(1);
expectTypeOf(Signal.State<number>).toBeConstructibleWith(1, {});
expectTypeOf(Signal.State<number>).toBeConstructibleWith(1, {equals: (a, b) => true});
expectTypeOf(Signal.State<number>).toBeConstructibleWith(1, {[Signal.subtle.watched]: () => true});
expectTypeOf(Signal.State<number>).toBeConstructibleWith(1, {
[Signal.subtle.unwatched]: () => true,
});
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith(() => 2);
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith(() => 1, {equals: (a, b) => true});
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith(() => 1, {
[Signal.subtle.watched]: () => true,
});
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith(() => 1, {
[Signal.subtle.unwatched]: () => true,
});

// @ts-expect-error
expectTypeOf<Signal.State<number>>().toBeConstructibleWith();
// @ts-expect-error
expectTypeOf(Signal.State<number>).toBeConstructibleWith('wrong', {});
expectTypeOf(Signal.State<number>).toBeConstructibleWith(1, {
// @ts-expect-error
[Signal.subtle.watched]: 2,
});
expectTypeOf(Signal.State<number>).toBeConstructibleWith(1, {
// @ts-expect-error
[Signal.subtle.unwatched]: 2,
});
expectTypeOf(Signal.State<number>).toBeConstructibleWith(1, {
// @ts-expect-error
typo: (a, b) => true,
});
// @ts-expect-error
expectTypeOf<Signal.Computed<number>>().toBeConstructibleWith();
// @ts-expect-error
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith('wrong');
// @ts-expect-error
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith(2);
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith(() => 1, {
// @ts-expect-error
[Signal.subtle.watched]: 2,
});
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith(() => 1, {
// @ts-expect-error
[Signal.subtle.unwatched]: 2,
});
expectTypeOf(Signal.Computed<number>).toBeConstructibleWith(() => 1, {
// @ts-expect-error
typo: (a, b) => true,
});

/**
* Properties on each of the instances / namespaces
*/
expectTypeOf<keyof Signal.State<unknown> & string>().toEqualTypeOf<'get' | 'set'>();
expectTypeOf<keyof Signal.Computed<unknown> & string>().toEqualTypeOf<'get'>();
expectTypeOf<keyof typeof Signal.subtle>().toEqualTypeOf<
| 'untrack'
| 'currentComputed'
| 'introspectSources'
| 'introspectSinks'
| 'hasSinks'
| 'hasSources'
| 'Watcher'
| 'watched'
| 'unwatched'
>();

expectTypeOf<keyof Signal.subtle.Watcher & string>().toEqualTypeOf<
'watch' | 'unwatch' | 'getPending'
>();

/**
* Inference works
*/
expectTypeOf(new Signal.State(0)).toEqualTypeOf<Signal.State<number>>();
expectTypeOf(new Signal.State(0).get()).toEqualTypeOf<number>();
expectTypeOf(new Signal.State(0).set(1)).toEqualTypeOf<void>();
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"lib": ["DOM", "ES2021"],
"strict": true,
"composite": true,
"forceConsistentCasingInFileNames": true
"forceConsistentCasingInFileNames": true,
"allowImportingTsExtensions": true
},
"exclude": ["**/node_modules/**", "**/*.spec.ts", "**/dist/**/*"],
"include": ["src"]
Expand Down

0 comments on commit c38a015

Please sign in to comment.