@coderspirit/名义上
Nominal 提供有力的工具包
名义打字 打开
字体 零运行时间高空.
它提供三种名义类型:
- 品牌: 品牌基本符合传统的名义打字概念. 品牌值只能属于一个 品牌,仅标出变量 接受相同数值 品牌。 。 。 .
- 火焰 : 火焰 和品牌类似,只有一个区别: 有口味 变量还接受相同无品牌/无习惯值 基准类型。 。 。 . 它们在处理"刚性"代码生成器或其他情况时非常有用 在那里,我们将被迫 写出吨的绘图 只是满足类型 检查器.
- 属性 : 说出逻辑和 数学属性,但也执行一种弱形式的 依赖类型。 。 。 .
虽然每种类型只能有一个 品牌 或一个 风味我们很容易 组合 品牌 或者说 口味 与 属性。 。 。 .
安装指令
节点
# With NPM
npm install --save-dev @coderspirit/nominal
# Or with PNPM
pnpm add --save-dev @coderspirit/nominal
# Or with Yarn:
yarn add --dev @coderspirit/nominal
[TIP] 请注意, 如果您正在开发一个应用程序, 安装是好的
@coderspirit/nominal作为发展依赖,但可能有必要 以正常或对等的依赖方式安装,以防您正在开发您的 拥有基于它的图书馆.
品牌
import { WithBrand } from '@coderspirit/nominal'
type Email = WithBrand<string, 'Email'>
type Username = WithBrand<string, 'Username'>
const email: Email = '[email protected]' as Email // Ok
const user: Username = 'admin' as Username // Ok
const text: string = email // OK
const anotherText: string = user // Ok
const eMail: Email = '[email protected]' // Error, as we don't have a cast here
const mail: Email = user // Error, as the brands don't match
咨询
- 虽然我们在这里表演"静态的铸造",但只有在以下情况下才能这样做:
- 值为字面( 如示例)
- 在鉴定、消毒和/或反腐败层面.
- 一种防止其他开发者“伪造”类型的方法是使用 符号而不是字符串作为属性键或属性值定义时 新的名义类型.
火焰
import { WithFlavor } from '@coderspirit/nominal'
type Email = WithFlavor<string, 'Email'>
type Username = WithFlavor<string, 'Username'>
const email: Email = '[email protected]' as Email // Ok
const user: Username = 'admin' as Username // Ok
const text: string = email // OK
const anotherText: string = user // Ok
const eMail: Email = '[email protected]' // Ok, flavors are more flexible than brands
const mail: Email = user // Error, as the flavors don't match
咨询
- 虽然我们在这里表演"静态的铸造",但只有在以下情况下才能这样做:
- 值为字面( 如示例)
- 在鉴定、消毒和/或反腐败层面.
- 一种防止其他开发者“伪造”类型的方法是使用 符号而不是字符串作为属性键或属性值定义时 新的名义类型.
更快的品牌和口味
类型 WithBrand 财务报告和审定财务报表 WithFlavor虽然目的很简单,
隐藏一个非常复杂的机器 存在的唯一目的就是维护
与其他更复杂的类型完全兼容,例如 WithProperty。 。 。 .
多数时候我们不需要依赖 如此复杂的机制,因为我们
应用 WithBrandh 财务报告和审定财务报表 WithFlavor 基本类型。 所以,如果我们想尽量减少
我们的汇编类型,我们可以选择一个更简单、更快的执行:
import {
FastBrand,
FastFlavor,
WithBrand,
WithFlavor
} from '@coderspirit/nominal'
// These two types are 100% equivalent, but the second one takes less time to be
// compiled. Notice that they are 100% equivalent only because they were applied
// to "basic" types (without other associated metadata, like `WithProperty`).
type SlowEmailType = WithBrand<string, 'Email'>
type FastEmailType = FastBrand<string, 'Email'>
// Same for flavors.
type SlowPhoneNumberType = WithFlavor<string, 'PhoneNumber'>
type FastPhoneNumberType = FastFlavor<string, 'PhoneNumber'>
属性
导言
要定义带有属性的新类型,我们可以做到:
import { WithProperty } from '@coderspirit/nominal'
type Even = WithProperty<number, 'Parity', 'Even'>
const myEven: Even = 42 as Even
如果我们想将这些属性用作简单的标记, 我们可以省略属性值,
它会默认 true,虽然它不太灵活:
import { WithProperty } from '@coderspirit/nominal'
type Positive = WithProperty<number, 'Positive'>
const myPositive: Positive = 1 as Positive
有意思,你这混蛋 属性
WithProperty具有添加剂、共通性和一能性.- 前一点意味着我们不必担心 我们不会因为这个而出现打字不一致的情况.
WithProperty 两者完全兼容:
- "经典"
&类型运算符 :type PositiveEven = WithProperty<number, 'Parity', 'Even'> & WithProperty<number, 'Positive'> - 巢穴类型 :
type PositiveEven = WithProperty<WithProperty<number, 'Positive'>, 'Parity', 'Even'>
咨询
- 虽然我们在这里表演"静态的铸造",但只有在以下情况下才能这样做:
- 值为字面( 如示例)
- 在鉴定、消毒和/或反腐败层面.
- 一种防止其他开发者“伪造”类型的方法是使用 符号而不是字符串作为属性键或属性值定义时 新的名义类型.
疯狂的严格
如果我们愿意,我们甚至可以定义"财产类型",以确保我们不设定 无效值 :
import { PropertyTypeDefinition, WithStrictProperty } from '@coderspirit/nominal'
type Parity = PropertyTypeDefinition<'Parity', 'Even' | 'Odd'>
// == WithProperty<number, 'Parity', 'Even'>
type Even = WithStrictProperty<number, Parity, 'Even'>
// == never
type Wrong = WithStrictProperty<number, Parity, 'Seven'>
高级使用型号( 依赖性类型)
属性可以跨函数边界保存
当我们需要验证许多属性时,这个特性可能非常有用 同样的价值,我们不想失去 这条路上的信息与价值 从一个函数传递到另一个函数.
function throwIfNotEven<T extends number>(v: T): WithProperty<T, 'Parity', 'Even'> {
if (v % 2 == 1) throw new Error('Not Even!')
return v as WithProperty<T, 'Even'>
}
function throwIfNotPositive<T extends number>(v: T): WithProperty<T, 'Sign', 'Positive'> {
if (v <= 0) throw new Error('Not positive!')
return v as WithProperty<T, 'Positive'>
}
const v1 = 42
// typeof v2 === WithProperty<number, 'Parity', 'Even'>
const v2 = throwIfNotEven(v1)
// typeof v3 extends WithProperty<number, 'Parity', 'Even'>
// typeof v3 extends WithProperty<number, 'Sign', 'Positive'>
const v3 = throwIfNotPositive(v2)
选择要跨越函数界限保存哪些属性
在前一个例子中,我们可以增加许多属性,因为我们只是 对价值做出断言。 当我们改变过去的价值观时,我们必须 注意我们保存的东西.
作为我们在这里所说的话的一个简单例子,我们可以看到增加 1 改为
数字变量会翻转它的等分, 因此在这种情况下,我们不想保持
返回值上的财产.
type Even<N extends number = number> = WithProperty<N, 'Parity', 'Even'>
type Odd<N extends number = number> = WithProperty<N, 'Parity', 'Odd'>
// 1. 'Parity' is overwritten (when available)
// 2. 'Sign' is kept only if it's positive
// 3. We discard all other properties because they might stop being true
type PlusOneResult<N> = KeepProperties<
N extends Even
? KeepPropertyIfValueMatches<Odd<N>, 'Sign', 'Positive'>
: N extends Odd
? KeepPropertyIfValueMatches<Even<N>, 'Sign', 'Positive'>
: KeepPropertyIfValueMatches<N, 'Sign', 'Positive'>,
'Sign' | 'Parity'
>
function plusOne<N extends number>(v: N): PlusOneResult<N> {
return v + 1 as PlusOneResult<N>
}