-
Notifications
You must be signed in to change notification settings - Fork 323
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
30 changed files
with
724 additions
and
1,210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** @file TypeScript's closest equivalent of `newtype`s. */ | ||
|
||
interface NewtypeVariant<TypeName extends string> { | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
_$type: TypeName | ||
} | ||
|
||
/** Used to create a "branded type", | ||
* which contains a property that only exists at compile time. | ||
* | ||
* `Newtype<string, 'A'>` and `Newtype<string, 'B'>` are not compatible with each other, | ||
* however both are regular `string`s at runtime. | ||
* | ||
* This is useful in parameters that require values from a certain source, | ||
* for example IDs for a specific object type. | ||
* | ||
* It is similar to a `newtype` in other languages. | ||
* Note however because TypeScript is structurally typed, | ||
* a branded type is assignable to its base type: | ||
* `a: string = asNewtype<Newtype<string, 'Name'>>(b)` successfully typechecks. */ | ||
export type Newtype<T, TypeName extends string> = NewtypeVariant<TypeName> & T | ||
|
||
interface NotNewtype { | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
_$type?: never | ||
} | ||
|
||
export function asNewtype<T extends Newtype<unknown, string>>( | ||
s: NotNewtype & Omit<T, '_$type'> | ||
): T { | ||
// This cast is unsafe. | ||
// `T` has an extra property `_$type` which is used purely for typechecking | ||
// and does not exist at runtime. | ||
// | ||
// The property name is specifically chosen to trigger eslint's `naming-convention` lint, | ||
// so it should not be possible to accidentally create a value with such a type. | ||
// eslint-disable-next-line no-restricted-syntax | ||
return s as unknown as T | ||
} |
Oops, something went wrong.