首页 > 解决方案 > 我可以根据全局方法的操作指定 TypeScript 类型约束吗?

问题描述

例如,我可以定义一个字符串类型吗

type BloodPressureMeasurementString = string;

和一个对象类型

type BloodPressureMeasurement = { systolic: number; diastolic: number; }

并指定JSON.parse()在字符串类型上产生对象类型,反之亦然JSON.stringify()

let bp: BloodPressureMeasurement = { systolic: 120, diastolic: 80 };
let bpString = JSON.stringfy(bp); // type of bpString is BloodPressureMeasurementString
let bpCopy = JSON.parse(bpString); // type of bpCopy is BloodPressureMeasurement

目前,这是通过JSON.parse()/JSON.stringify()调用站点的类型断言或强制代码完成的。我希望能够将这些移动到创建字符串资源的位置(例如从 Internet 下载时)。

标签: typescript

解决方案


有趣的问题!

让我们看看如何安全地做到这一点。

在此处输入图像描述

type BloodPressureMeasurementString = string;

type BloodPressureMeasurement = { systolic: number; diastolic: number; }

interface JSON { // extends JSON interface through declaration merging
    stringify(val: BloodPressureMeasurement): BloodPressureMeasurementString
    parse(val: BloodPressureMeasurementString): BloodPressureMeasurement
}

var a = JSON.stringify({ systolic: 2, diastolic: 3 }); //string
var b = JSON.parse('{"systolic":2,"diastolic":3}'); //BloodPressureMeasurement
var c = JSON.parse('{ "name":"John", "age":30, "city":"New York"}'); //BloodPressureMeasurement!!

并没有真正按预期工作 =/ 你可能已经知道,Typescript 类型系统是结构性的而不是名义上的。BloodPressureMeasurementString和之间没有任何区别string。所以我们需要一种模拟名义类型的方法。

让我们通过一些调整来做到这一点1

type BloodPressureMeasurementString = string & {readonly brand: unique symbol};

你有它,a现在是 type BloodPressureMeasurementString

Typescript 足够聪明,可以推断出哪个重载更相关:

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

但是,请注意,您将无法执行此类操作

var variable: BloodPressureMeasurementString = "some string" //type '"some string"' is not assignable to type 'BloodPressureMeasurementString'

这是意料之中的。您必须使用类型断言。我们可以使用一个函数来帮助我们创建一个 BloodPressureMeasurementString。

//types and values live in separate namespaces so we can have the same name both for the function and for the type
function BloodPressureMeasurementString(str: string) { 
    return str as BloodPressureMeasurementString
}

然后我们可以做

var variable: BloodPressureMeasurementString = BloodPressureMeasurementString("some string") // OK

打字稿游乐场

1您可能想知道 {readonly brand: unique symbol} 是什么。它的目的是双重的:首先,我们不希望我们的类型与字符串混淆,我们也不希望它与相似的类型混淆。我们不能写出结果永远不会的字符串和数字,如果我们写成 string & { },它会与字符串混淆,如果我们写 string & { brand: symbol },另一个类似的类型(命名不同但有字符串& { brand: symbol }) 可能与我们的类型混淆。唯一的(需要只读)将不允许这样做。


推荐阅读