Skip to content

Commit

Permalink
feat: implement TypeScript transformer to auto-implement argument che…
Browse files Browse the repository at this point in the history
…cks based on types (#4394)
  • Loading branch information
AlCalzone authored Mar 21, 2022
1 parent 90eae72 commit 4306cd4
Show file tree
Hide file tree
Showing 30 changed files with 5,763 additions and 10 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@types/node": "^15.12.5",
"@types/semver": "^7.3.9",
"@types/source-map-support": "^0",
"@types/ts-nameof": "^4.2.1",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"@zwave-js/config": "workspace:*",
Expand All @@ -75,6 +76,8 @@
"reflect-metadata": "^0.1.13",
"semver": "^7.3.5",
"source-map-support": "^0.5.21",
"ts-nameof": "^5.0.0",
"ts-patch": "^2.0.1",
"typescript": "4.6.2",
"zwave-js": "workspace:*"
},
Expand Down Expand Up @@ -103,7 +106,8 @@
"commit": "git-cz",
"release": "release-script",
"release:all": "release-script --publish-all",
"postinstall": "husky install",
"postinstall": "husky install ; ts-patch install -s",
"prepare": "ts-patch install -s",
"config": "yarn ts packages/config/maintenance/importConfig.ts",
"docs": "docsify serve docs",
"docs:generate": "yarn ts packages/maintenance/src/generateTypedDocs.ts",
Expand Down
3 changes: 0 additions & 3 deletions packages/maintenance/.gitignore

This file was deleted.

3 changes: 3 additions & 0 deletions packages/maintenance/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"references": [
{
"path": "../core/tsconfig.build.json"
},
{
"path": "../shared/tsconfig.build.json"
}
],
"include": ["src/**/*.ts"],
Expand Down
3 changes: 3 additions & 0 deletions packages/maintenance/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"references": [
{
"path": "../core/tsconfig.build.json"
},
{
"path": "../shared/tsconfig.build.json"
}
],
"include": ["src/**/*.ts"],
Expand Down
41 changes: 41 additions & 0 deletions packages/transformers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "@zwave-js/transformers",
"version": "9.0.0-beta.7",
"description": "zwave-js: compile-time transformers",
"private": true,
"keywords": [],
"main": "build/index.js",
"types": "build/index.d.ts",
"files": [
"build/**/*.{js,d.ts,map}"
],
"author": {
"name": "AlCalzone",
"email": "[email protected]"
},
"license": "MIT",
"homepage": "https://github.com/AlCalzone/node-zwave-js#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/AlCalzone/node-zwave-js.git"
},
"bugs": {
"url": "https://github.com/AlCalzone/node-zwave-js/issues"
},
"funding": {
"url": "https://github.com/sponsors/AlCalzone/"
},
"engines": {
"node": ">=12.22.2 <13 || >=14.13.0 <15 || >= 16 <16.9.0 || >16.9.0"
},
"scripts": {
"build": "tsc -b tsconfig.build.json --verbose",
"clean": "yarn run build --clean",
"watch": "yarn run build --watch --pretty",
"test": "yarn run build && tsc -p tsconfig.test.json; node test/test1"
},
"devDependencies": {
"tsutils": "^3.21.0",
"typescript": "4.6.2"
}
}
62 changes: 62 additions & 0 deletions packages/transformers/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// function checkGetErrorObject(
// getErrorObject: unknown,
// ): asserts getErrorObject is (obj: any) => any {
// if (typeof getErrorObject !== "function") {
// throw new Error(
// "This module should not be used in runtime. Instead, use a transformer during compilation.",
// );
// }
// }

// /**
// * Checks if the given argument is assignable to the given type-argument.
// *
// * @param object object whose type needs to be checked.
// * @returns `true` if `object` is assignable to `T`, false otherwise.
// * @example
// ```
// is<number>(42); // -> true
// is<number>('foo'); // -> false
// ```
// */
// export function is<T>(object: any): object is T;
// export function is<T>(obj: any, getErrorObject?: (obj: any) => any): obj is T {
// checkGetErrorObject(getErrorObject);
// const errorObject = getErrorObject(obj);
// return errorObject === null;
// }

// /**
// * Creates a function similar to `is<T>` that can be invoked at a later point.
// *
// * This is useful, for example, if you want to re-use the function multiple times.
// *
// * @example
// ```
// const checkNumber = createIs<number>();
// checkNumber(42); // -> true
// checkNumber('foo'); // -> false
// ```
// */
// export function createIs<T>(): (object: any) => object is T;
// export function createIs<T>(
// getErrorObject = undefined,
// ): (object: any) => object is T {
// checkGetErrorObject(getErrorObject);
// // @ts-expect-error We're using an internal signature
// return (obj) => is(obj, getErrorObject);
// }

/** Generates code at build time which validates all arguments of this method */
export function validateArgs(): PropertyDecorator {
return (_target: unknown, _property: string | number | symbol) => {
// this is a no-op that gets replaced during the build process using the transformer below
// Throw an error when this doesn't get transformed
throw new Error(
"validateArgs is a compile-time decorator and must be compiled with a transformer",
);
};
}

import transformer from "./validateArgs/transformer";
export default transformer;
18 changes: 18 additions & 0 deletions packages/transformers/src/validateArgs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# validateArgs

TypeScript transformer that generates run-time type-checks, based on https://github.com/woutervh-/typescript-is

Usage:

```ts
import { validateArgs } from "@zwave-js/transformers";

class Test {
@validateArgs()
foo(arg1: number, arg2: Foo, arg3: Foo & Bar): void {
// implementation
}
}
```

The import and the decorator call will be removed and the function body of `foo` will be prepended with assertions for each of the arguments.
119 changes: 119 additions & 0 deletions packages/transformers/src/validateArgs/reason.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
export interface ExpectedFunction {
type: "function";
}

export interface ExpectedString {
type: "string";
}

export interface ExpectedNumber {
type: "number";
}

export interface ExpectedBigInt {
type: "big-int";
}

export interface ExpectedBoolean {
type: "boolean";
}

export interface ExpectedStringLiteral {
type: "string-literal";
value: string;
}

export interface ExpectedNumberLiteral {
type: "number-literal";
value: number;
}

export interface ExpectedBooleanLiteral {
type: "boolean-literal";
value: boolean;
}

export interface ExpectedObject {
type: "object";
}

export interface ExpectedDate {
type: "date";
}

export interface ExpectedNonPrimitive {
type: "non-primitive";
}

export interface MissingObjectProperty {
type: "missing-property";
property: string;
}

export interface SuperfluousObjectProperty {
type: "superfluous-property";
}

export interface ExpectedObjectKeyof {
type: "object-keyof";
properties: string[];
}

export interface ExpectedArray {
type: "array";
}

export interface NeverType {
type: "never";
}

export interface ExpectedTuple {
type: "tuple";
minLength: number;
maxLength: number;
}

export interface NoValidUnionAlternatives {
type: "union";
}

export interface ExpectedUndefined {
type: "undefined";
}

export interface ExpectedNull {
type: "null";
}

export type TemplateLiteralPair = [
string,
"string" | "number" | "bigint" | "any" | "undefined" | "null" | undefined,
];

export interface ExpectedTemplateLiteral {
type: "template-literal";
value: TemplateLiteralPair[];
}

export type Reason =
| ExpectedFunction
| ExpectedString
| ExpectedNumber
| ExpectedBigInt
| ExpectedBoolean
| ExpectedObject
| ExpectedDate
| ExpectedNonPrimitive
| MissingObjectProperty
| SuperfluousObjectProperty
| ExpectedObjectKeyof
| ExpectedArray
| ExpectedTuple
| NeverType
| NoValidUnionAlternatives
| ExpectedUndefined
| ExpectedNull
| ExpectedStringLiteral
| ExpectedNumberLiteral
| ExpectedBooleanLiteral
| ExpectedTemplateLiteral;
Loading

0 comments on commit 4306cd4

Please sign in to comment.