Typescript 如何使用'px'后缀定义自定义类型

Typescript 如何使用'px'后缀定义自定义类型,typescript,Typescript,我知道如何定义位置类型类: 类位置{ x:数字=0; y:数字=0; } 但是现在我需要知道x和y值是一个后缀为px的整数,如下所示: const位置={ x:'1px', y:'2px' } 如何在TypeScript中定义这样的类型?您可以像以前一样定义具有数字x和y属性的类型,或者定义另一个同时允许数字和字符串的类型 class Position { x: number | string = 0; y: number | string = 0; } 你不能。这将是一个不仅在T

我知道如何定义
位置
类型类:

类位置{
x:数字=0;
y:数字=0;
}
但是现在我需要知道
x
y
值是一个后缀为
px
的整数,如下所示:

const位置={
x:'1px',
y:'2px'
}

如何在TypeScript中定义这样的类型?

您可以像以前一样定义具有数字x和y属性的类型,或者定义另一个同时允许数字和字符串的类型

class Position {
  x: number | string = 0;
  y: number | string = 0;
}

你不能。这将是一个不仅在Typescript中不存在,而且你必须在普通语言的范围之外寻找的问题。最著名的具有依赖类型的语言可能是

就实际解决问题而言,最好的解决方案可能是这样的:

const VALID_XY_VAL = /^(-?\d+)px$/;

class Point {
  _x: number = 0;
  _y: number = 0;

  constructor(x: string, y: string) {
    // NOTE: not _x and _y, we want to invoke the
    // setters here
    this.x = x;
    this.y = y;
  }

  get x () {
    return `${this._x}px`;
  }

  get y () {
    return `${this._y}px`;
  }

  set x(xVal: string) {
    const num = xVal.match(VALID_XY_VAL);
    if (num === null) throw new Error('Invalid value');
    this._x = Number(num[1]);
  }

  set y(yVal: string) {
    const num = yVal.match(VALID_XY_VAL);
    if (num === null) throw new Error('Invalid value');
    this._y = Number(num[1]);
  }
}

const p = new Point('1px', '2px'); // Point<1, 2>
p.x; // '1px'
p.y; // '2px'
p.x = '5px'; // a-ok
p.y = '5ac3px'; // KABOOM! Error.
编辑,推理答案 你们中的一些人可能认为这不是给定问题的答案。但现实是,作为开发人员,我们有责任为数据结构建模。发问者在这里提出了一个非常具体的问题,关于如何在typescript中建模
“{number}px”
类型答案是一-这是不可能的。但问题是这样的,因为发问者认为这种类型是解决他的问题的方法,这是一种误导。真正的问题是如何在类型系统中用两个数值x、y和单位表示2D点。这可以通过简单的
[数字,数字,'px']
来实现,我们说我们想要两个数字,我们的单位是静态的'px'。这种结构灵活,型式安全。使用一些regexp,带有setter和getter并引发异常的类与编译步骤无关,而是复杂的运行时验证

原始答复: 为什么需要一个类来表示一对。将类用于如此简单的构造就像用锤子打死苍蝇一样。您真正需要的是简单的pair+unit表示,它可以表示为tuple
[a,b,unit]
或record
{a:a,b:b,unit:unit}
。正如其他人也指出的,不可能定义带有后缀“px”的数字的类型,但可以通过多种方式对此进行建模。几点主张:


1.方案一-附加单位信息建模

// modeling as 3-nd tuple
type Point = [number, number, string]
const point = [1,2,'px']

// modeling as key-value map 
type Point = {x: number, y: number, unit: string}
const point = {x:1,y:2,unit:'px'}

2.采用严格的单元类型进行建模。 如果我们确定会有像‘px’这样的严格单位,它可以在类型中定义。为了不重复,我将仅在键值映射类型中显示示例

type Point = {x: number, y: number, unit: 'px'} // unit property can be only px
const point = {x:1,y:2,unit:'px'}
我们还可以创建点构造函数,以避免手动放置px:

const createPoint = (x: number, y: number):Point => ({x,y,unit:'px'});
const point = createPoint(1,2) // {x:1,y:2,unit:'px'}

3.建模为一对字符串,但使用构造函数 我们可以将类型保留为字符串对,但可以通过数字构造函数生成它

type Point = {x: string, y: string}
const createPoint = (x: number, y: number):Point => ({x: x + 'px', y: y + 'px'});
const point = createPoint(1,2) // {x:'1px',y:'2px'}

4.作为具有特殊get函数的对象进行建模

type Point = {values: () => {x: number, y: number}, pixels: () => {x: string, y: string}}
// below is using closure and stored in it arguments of createPoint function
const createPoint = (x:number, y:number): Point => ({
  values: () => ({x, y}),
  pixels: () => ({x: x + 'px', y: y + 'px'})
})
const point = createPoint(1,2);
point.values() // {x: 1, y: 2}
point.pixels() // {x: '1px', y: '2px'}

你不能。这将是一个不仅在Typescript中不存在,而且你必须在普通语言的范围之外寻找的问题。最著名的具有依赖类型的语言可能是。只需在运行时检查这些值即可。@JaredSmith您完全不需要依赖类型来实现OP想要的内容-用户定义的类型文字就足够了,许多现代语言都提供了它们。然后OP所要做的就是写
1px
,而不是
'1px'
,就我所理解的问题而言,那就好了。作为一个简单的解决方法,定义构造函数
px
可能离OP想要实现的目标还有很长的路要走。@KonradRudolph定义px构造函数会很好,但是在Typescript中没有类型文字。我的答案是一个相当惯用的JS/TS解决方案。可以使用sweet.js宏来实现所需的逻辑,但这会带来大量的复杂性开销,而性能方面的好处却微乎其微!这种类型的事情现在是可能的。这并不能回答问题,这是关于如何在编译时将字符串的值静态地限制为以“px”结尾的值。进一步微调以更好地封装内容,
VALID_XY_VAL
可以是类
Point
的常量成员:
私有静态只读有效_XY_VAL=/^(-?\d+)px$/@altocumulus呃,那可能是杀伤力过大了。它声明为const(不能重新赋值),并且regex文本在任何重要的方面都是不可变的。还是我遗漏了什么?OOP的基本原则之一!保持它尽可能紧密;无需对全局命名空间进行处理。您可能希望在不久的将来更改检查值的方式,而不会有任何副作用,也不会有任何人受到影响,甚至不会有任何人知道您进行了更改。只要我的两分钱。@altocumulus类型脚本编译器使用ES 2015模块系统。没有全局名称空间污染,并且没有导出正则表达式,因此任何更改(例如,从正则表达式到子字符串检查)对导入程序都是不可见的,除非通过更改设置程序的行为(假设已更改)。如果这是在库中,您甚至不需要重新编译调用代码。我主要来自FP背景,并且隐藏一个不可变的常量值,在模块范围之外无法访问,这似乎。。。单一的?IDK.@altocumulus我在答案中添加了一个关于它的编辑。我同意一个类对于一个2d点几乎肯定是多余的(如果是我的话,我只会使用一个带ints的POJO和一个合适的接口),我只是回答了问题。但我不认为类对于OP希望实现的逻辑来说是过分的:它可读性强,性能可能更高,与Typescript的类型系统配合得很好,等等。至于运行时检查,一旦严格键入,您的选项就非常有限(这就是为什么我坚持使用数字,但再次回答了问题)。
type Point = {values: () => {x: number, y: number}, pixels: () => {x: string, y: string}}
// below is using closure and stored in it arguments of createPoint function
const createPoint = (x:number, y:number): Point => ({
  values: () => ({x, y}),
  pixels: () => ({x: x + 'px', y: y + 'px'})
})
const point = createPoint(1,2);
point.values() // {x: 1, y: 2}
point.pixels() // {x: '1px', y: '2px'}