在TypeScript中声明具有最小/最大长度的字符串类型

31

经过查阅文档,似乎没有直接检查字符串数据类型最小/最大长度的方法。

但是,是否有一种使用自定义类型声明字符串数据类型的方法,以便检查字符串长度是否在给定范围内?


类型就是类型,它们在运行时甚至都不存在,这只是JavaScript而已。类型无法检查字符串是否具有预期长度,您需要编写代码来实现。 - JB Nizet
1
@JBNizet 我知道静态类型检查在运行时没有帮助。但是声明类型有助于在代码的其他位置调用函数时检测错误的数据类型。而且由于tsc不仅作为类型检查器,还作为转译器,我希望能找到一些解决方法来实现这个功能。 - Deepak
3
@PatrickRoberts 我有一个非常简单的用例。只有一个函数,输入为字符串。但我希望对其长度设限,并在调用时使我的编辑器警告我是否违反了这个规则。我不认为有任何直接的解决方案,但如果有某种方式可以实现,那就太好了。 - Deepak
目前已经有一些建议支持在类型系统中进行一些字符串验证,但是还没有任何东西被纳入到该语言中。 - jcalz
使用品牌类型,您可以强制消费者进行测试以验证字符串的有效长度。我在这里回答了类似的问题:https://dev59.com/tarka4cB1Zd3GeqPc2E2#49673307 - Titian Cernicova-Dragomir
显示剩余2条评论
1个回答

46

你可以通过使用类型构造器和所谓的“幻影类型”( 在此处阅读有关此技术的优秀文章)来实现此目标,这是一种确保类型无法直接赋值给值的技术。

以下是使用这些技术的StringOfLength<Min,Max>类型的示例:

type StringOfLength<Min, Max> = string & {
  min: Min;
  max: Max;
  StringOfLength: unique symbol // this is the phantom type
};

// This is a type guard function which can be used to assert that a string
// is of type StringOfLength<Min,Max>
const isStringOfLength = <Min extends number, Max extends number>(
  str: string,
  min: Min,
  max: Max
): str is StringOfLength<Min, Max> => str.length >= min && str.length <= max;
    
// type constructor function
export const stringOfLength = <Min extends number, Max extends number>(
  input: unknown,
  min: Min,
  max: Max
): StringOfLength<Min, Max> => {
  if (typeof input !== "string") {
    throw new Error("invalid input");
  }
    
  if (!isStringOfLength(input, min, max)) {
    throw new Error("input is not between specified min and max");
  }
    
  return input; // the type of input here is now StringOfLength<Min,Max>
};

// Now we can use our type constructor function
const myString = stringOfLength('hello', 1, 10) // myString has type StringOfLength<1,10>

// the type constructor fails if the input is invalid
stringOfLength('a', 5, 10) // Error: input is not between specified min and max

// The phantom type prevents us from assigning StringOfLength manually like this:
const a: StringOfLength<0, 10> = 'hello' // Type '"hello"' is not assignable to type { StringOfLength: unique symbol }

这里有一些限制 - 你不能阻止某人创建一个无效的类型,比如 StringOfLength<-1, -300>,但是你可以添加运行时检查,确保传递给 stringOfLength 构造函数的 minmax 值是有效的。

编辑:在 TypeScript 中,这种技术现在更常被称为“品牌类型”。


它抛出了一个错误:接口或类型文字的属性,其类型为“唯一符号”类型,必须是“只读”的 - TOPKAT
1
也许这是较新版本的TypeScript中的错误。如果您添加readonly修饰符,应该能够解决该错误:readonly StringOfLength: unique symbol - cdimitroulas
值得一提的是,即使使用了TypeScript,它也不会在编译时检查字符串长度,它只会在运行时告诉您字符串是否超出范围。 - benmneb
具体取决于您的意思。在编译时,它将防止您使用“未经检查”的字符串(类型为string),而不是StringOfLength。它还将防止您使用错误长度的字符串,例如StringOfLength <1,100>不能分配给StringOfLength <1,50> - cdimitroulas
1
由于某些原因,它似乎无法在编译时验证。我能够将 StringOfLength<x, y> 分配给 StringOfLength<a, b>,其中 x != ay != b,但我没有看到任何编译错误。https://tsplay.dev/NnEExw - vighnesh153
谢谢@vighnesh153,我已经更新了代码来修复这个问题。为了使其正常工作,MinMax类型参数也需要包含在最终类型中。https://tsplay.dev/mZZJKm - cdimitroulas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接