diff --git a/package.json b/package.json index e37cdc7..d94d5bf 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,9 @@ "compile:src": "tsc -p src", "lint:codegen": "tslint -p codegen/tsconfig.json -c tslint.json", "lint:src": "tslint -p src/tsconfig.json -c tslint.json", - "lint:types": "./scripts/check-typings.sh", - "lint": "npm-run-all -p lint:codegen lint:src lint:types", + "lint:dist": "./scripts/check-typings.sh", + "lint:types": "dtslint test/types", + "lint": "npm-run-all -p lint:codegen lint:src lint:dist lint:types", "node:codegen": "node codegen/dist/emit", "test": "jest --config jest.config.json", "prepack": "yarn build", @@ -36,6 +37,7 @@ "devDependencies": { "@types/jest": "^22.2.3", "@types/node": "^8.10.16", + "dtslint": "^0.3.0", "jest": "^22.4.3", "npm-run-all": "^4.1.3", "prettier": "^1.12.1", diff --git a/src/exponent/exponentArithmetic.ts b/src/exponent/exponentArithmetic.ts index e3be4fe..2f9f86c 100644 --- a/src/exponent/exponentArithmetic.ts +++ b/src/exponent/exponentArithmetic.ts @@ -4,6 +4,8 @@ import { DivideExponents } from "./generated/division"; import { Exponent } from "./generated/exponent"; import { MultiplyExponents } from "./generated/multiplication"; +export type NonZeroExponent = Exclude; + type SubtractExponents = AddExponents>; type Negative = MultiplyExponents; diff --git a/src/measure/unitTypeArithmetic.ts b/src/measure/unitTypeArithmetic.ts index 8fed632..3b66819 100644 --- a/src/measure/unitTypeArithmetic.ts +++ b/src/measure/unitTypeArithmetic.ts @@ -5,6 +5,7 @@ import { Exponent, MultiplicandOf, MultiplyExponents, + NonZeroExponent, ProductOf, } from "../exponent"; @@ -50,9 +51,9 @@ export interface BaseUnit { // Roots /** Returns the nth root of a unit. This is the inverse scalar multiple of the dimension vector. */ -export type NthRootUnit = StripZeroes< - { [Dim in keyof U]: DivideExponents, N> } ->; +export type NthRootUnit = 1 extends N + ? U + : { [Dim in keyof U]: DivideExponents, N> }; /** A type that is assignable from all units whose Nth root does not produce an error. */ export interface RadicandUnit { @@ -68,4 +69,4 @@ type StripZeroes = { [Dim in NonZeroKeys]: U[Dim] }; type NonZeroKeys = { [Dim in keyof U]: NonNullable extends 0 ? never : Dim }[keyof U]; /** Get the exponent at a given dimension of a unit, or 0 if that dimension is undefined */ -type GetExponent = D extends keyof Unit ? (undefined extends U[D] ? 0 : NonNullable) : 0; +export type GetExponent = D extends keyof U ? NonNullable : 0; diff --git a/src/measure/unitValueArithmetic.ts b/src/measure/unitValueArithmetic.ts index 5684c03..3f7dcd3 100644 --- a/src/measure/unitValueArithmetic.ts +++ b/src/measure/unitValueArithmetic.ts @@ -1,4 +1,4 @@ -import { Exponent } from "../exponent"; +import { Exponent, NonZeroExponent } from "../exponent"; import { BaseUnit, DivideUnits, @@ -70,7 +70,7 @@ export function exponentiateUnit, N extends Exponent>( return expAndRootImpl(unit, exponent => exponent * power); } -export function nthRootUnit, N extends Exponent>( +export function nthRootUnit, N extends NonZeroExponent>( unit: UnitWithSymbols, root: N, ): UnitWithSymbols> { diff --git a/test/types/index.d.ts b/test/types/index.d.ts new file mode 100644 index 0000000..38e99dd --- /dev/null +++ b/test/types/index.d.ts @@ -0,0 +1 @@ +// TypeScript Version: 2.9 diff --git a/test/types/measuresErrors.ts b/test/types/measuresErrors.ts new file mode 100644 index 0000000..a58fc3e --- /dev/null +++ b/test/types/measuresErrors.ts @@ -0,0 +1,16 @@ +import { Area, Measure, Volume } from "safe-units"; + +Volume.times(Volume); // $ExpectError +const volumeInverse = Volume.inverse(); +Volume.over(volumeInverse); // $ExpectError + +Volume.plus(Area); // $ExpectError +Area.minus(Volume); // $ExpectError + +const s = Volume.squared; // $ExpectType never +const c = Area.cubed; // $ExpectType never +Area.toThe(4); // $ExpectType never +Volume.toThe(-2); // $ExpectType never + +Measure.sqrt(Volume); // $ExpectError +Measure.cbrt(Area); // $ExpectError diff --git a/test/types/measuresValid.ts b/test/types/measuresValid.ts new file mode 100644 index 0000000..2ae77ef --- /dev/null +++ b/test/types/measuresValid.ts @@ -0,0 +1,26 @@ +import { Acceleration, kilograms, Measure, meters, newtons, seconds, Velocity } from "safe-units"; + +Measure.dimension("x"); // $ExpectType GenericMeasure + +const m = meters; // $ExpectType GenericMeasure +const s = seconds; // $ExpectType GenericMeasure +const n = newtons; // $ExpectType GenericMeasure> + +const a = newtons.over(kilograms); // $ExpectType GenericMeasure> +const accel: Acceleration = a; // $ExpectType GenericMeasure> + +const absement = meters.times(seconds); // $ExpectType GenericMeasure> +const velocity = meters.over(seconds); // $ExpectType GenericMeasure> + +meters.plus(meters); // $ExpectType GenericMeasure +meters.minus(meters); // $ExpectType GenericMeasure +meters.negate(); // $ExpectType GenericMeasure +meters.scale(2); // $ExpectType GenericMeasure + +velocity.squared(); // $ExpectType GenericMeasure +absement.cubed(); // $ExpectType GenericMeasure +absement.inverse(); // $ExpectType GenericMeasure +velocity.toThe(0); // $ExpectType GenericMeasure + +Measure.sqrt(velocity.toThe(-4)); // $ExpectType GenericMeasure +Measure.cbrt(absement.toThe(3)); // $ExpectType GenericMeasure diff --git a/test/types/tsconfig.json b/test/types/tsconfig.json new file mode 100644 index 0000000..ddd0445 --- /dev/null +++ b/test/types/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "lib": ["es5"], + "baseUrl": ".", + "paths": { "safe-units": ["../../src"] } + } +} \ No newline at end of file diff --git a/test/types/tslint.json b/test/types/tslint.json new file mode 100644 index 0000000..42701ce --- /dev/null +++ b/test/types/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "dtslint/dtslint.json", + "rules": { + "no-useless-files": false + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 93a0af1..8a6cddd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24,6 +24,11 @@ version "8.10.16" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.16.tgz#96fadb371748845a0c8ea970a565330efb0a67d5" +"@types/parsimmon@^1.3.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@types/parsimmon/-/parsimmon-1.10.0.tgz#ffb81cb023ff435a41d4710a29ab23c561dc9fdf" + integrity sha512-bsTIJFVQv7jnvNiC42ld2pQW2KRI+pAG243L+iATvqzy3X6+NH1obz2itRKDZZ8VVhN3wjwYax/VBGCcXzgTqQ== + abab@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" @@ -85,6 +90,11 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +any-promise@^1.0.0, any-promise@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" @@ -771,6 +781,13 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +definitelytyped-header-parser@Microsoft/definitelytyped-header-parser#production: + version "0.0.0" + resolved "https://codeload.github.com/Microsoft/definitelytyped-header-parser/tar.gz/6ff181ecd781706f200f8a780b0133a85fd0a69f" + dependencies: + "@types/parsimmon" "^1.3.0" + parsimmon "^1.2.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -810,6 +827,17 @@ domexception@^1.0.0: dependencies: webidl-conversions "^4.0.2" +dtslint@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dtslint/-/dtslint-0.3.0.tgz#918e664a6f7e1f54ba22088daa2bc6e64a6001c1" + integrity sha512-3oWL8MD+2nKaxmNzrt8EAissP63hNSJ4OLr/itvNnPdAAl+7vxnjQ8p2Zdk0MNgdenqwk7GcaUDz7fQHaPgCyA== + dependencies: + definitelytyped-header-parser Microsoft/definitelytyped-header-parser#production + fs-promise "^2.0.0" + strip-json-comments "^2.0.1" + tslint "^5.9.1" + typescript next + duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -1112,12 +1140,30 @@ fs-extra@6.0.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35" + integrity sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" dependencies: minipass "^2.2.1" +fs-promise@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fs-promise/-/fs-promise-2.0.3.tgz#f64e4f854bcf689aa8bddcba268916db3db46854" + integrity sha1-9k5PhUvPaJqovdy6JokW2z20aFQ= + dependencies: + any-promise "^1.3.0" + fs-extra "^2.0.0" + mz "^2.6.0" + thenify-all "^1.6.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1983,6 +2029,13 @@ json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -2244,6 +2297,15 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" +mz@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + nan@^2.9.2: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -2383,7 +2445,7 @@ oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -2518,6 +2580,11 @@ parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" +parsimmon@^1.2.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/parsimmon/-/parsimmon-1.12.0.tgz#886a442fb30b5fc3c8e7c4994050f5cdcfe0ea90" + integrity sha512-uC/BjuSfb4jfaWajKCp1mVncXXq+V1twbcYChbTxN3GM7fn+8XoHwUdvUz+PTaFtDSCRQxU8+Rnh+iMhAkVwdw== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -3158,7 +3225,7 @@ strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" -strip-json-comments@~2.0.1: +strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -3210,6 +3277,20 @@ test-exclude@^4.2.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" +thenify-all@^1.0.0, thenify-all@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.0" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" + integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= + dependencies: + any-promise "^1.0.0" + throat@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" @@ -3343,6 +3424,24 @@ tslint@^5.10.0: tslib "^1.8.0" tsutils "^2.12.1" +tslint@^5.9.1: + version "5.11.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" + integrity sha1-mPMMAurjzecAYgHkwzywi0hYHu0= + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.27.2" + tsutils@^1.4.0: version "1.9.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.9.1.tgz#b9f9ab44e55af9681831d5f28d0aeeaf5c750cb0" @@ -3353,6 +3452,13 @@ tsutils@^2.12.1: dependencies: tslib "^1.8.1" +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -3369,9 +3475,15 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +typescript@next: + version "3.2.0-dev.20181116" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.0-dev.20181116.tgz#0a405b3385d6f180df78bf557167c0a608b5a501" + integrity sha512-+UUHAGhfccJe1ZnHbqp0uixDjT/yMguznOvF1p+16ytQPJaFo2MfIt6on4PZlgl7VhdxtENSfzl44xxjWsfj8Q== + typescript@~2.9.1: version "2.9.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" + integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== uglify-js@^2.6: version "2.8.29"