Skip to content

Commit

Permalink
Add Opaque type (#53)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
Co-authored-by: Dimitri Benin <BendingBender@users.noreply.github.com>
  • Loading branch information
3 people committed Aug 3, 2019
1 parent 8b8dd4d commit 681f59b
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 0 deletions.
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Click the type names for complete docs.
- [`JsonArray`](source/basic.d.ts) - Matches a JSON array.
- [`JsonValue`](source/basic.d.ts) - Matches any valid JSON value.
- [`ObservableLike`](source/basic.d.ts) - Matches a value that is like an [Observable](https://github.com/tc39/proposal-observable).
- [`Opaque`](source/basic.d.ts) - Creates an [opaque type](https://codemix.com/opaque-types-in-javascript/).

### Utilities

Expand Down
41 changes: 41 additions & 0 deletions source/basic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,44 @@ export interface ObservableLike {
subscribe(observer: (value: unknown) => void): void;
[Symbol.observable](): ObservableLike;
}

/**
Creates an opaque type, which hides its internal details from the public, and can only be created by being used explicitly.
The generic type parameter can be anything. It doesn't have to be an object.
Read more about opaque types [here](https://codemix.com/opaque-types-in-javascript/).
There have been several discussions about adding this feature to TypeScript via the `opaque type` operator, similar to how Flow does it. Unfortunately, nothing has (yet) moved forward:
1. [](https://github.com/Microsoft/TypeScript/issues/15408)
2. [](https://github.com/Microsoft/TypeScript/issues/15807)
@example
```
import {Opaque} from 'type-fest';
type AccountNumber = Opaque<number>;
type AccountBalance = Opaque<number>;
function createAccountNumber(): AccountNumber {
return 2 as AccountNumber;
}
function getMoneyForAccount(accountNumber: AccountNumber): AccountBalance {
return 4 as AccountBalance;
}
// This will compile successfully:
getMoneyForAccount(createAccountNumber());
// But this won't, because it has to be explicitly passed as an AccountNumber.
getMoneyForAccount(2);
// You can use opaque values like they aren't opaque too, like this:
const accountNumber = createAccountNumber();
// This will compile successfully:
accountNumber + 2;
```
*/
type Opaque<Type> = Type & {readonly __opaque__: unique symbol};
13 changes: 13 additions & 0 deletions test-d/opaque.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {expectType} from 'tsd';
import {Opaque} from '..';

type Value = Opaque<number>;

// We make an explicit cast so we can test the value.
const value: Value = 2 as Value;

// Every opaque type should have a private symbol member, so the compiler can differentiate separate opaque types.
expectType<symbol>(value.__opaque__);

// The underlying type of the value is still a number.
expectType<number>(value);

0 comments on commit 681f59b

Please sign in to comment.