Dart -如何像在Typescript中一样动态地创建自定义类型?

soat7uwm  于 2023-10-13  发布在  TypeScript
关注(0)|答案(3)|浏览(121)

我想在Dart中创建一个自定义类型,就像在typescript中一样。此类型应该是String的子类型,只接受某些值。
例如,在Typescript中,我会这样做:

type myType = 'HELLO' | 'WORLD' | '!'

我怎么能在Dart中做同样的事情呢?

ryhaxcpt

ryhaxcpt1#

原始答案(见下面的更新):这在Dart的语言级别上是不可能的--不过有几个替代方案。

您可以简单地定义一个枚举沿着一个方法来从枚举中派生一个字符串:

enum MyType {
  hello,
  world,
  exclamationPoint,
}

String myTypeToString(MyType value) {
  switch (value) {
    case MyType.hello: 
      return 'HELLO';
    case MyType.world: 
      return 'WORLD';
    case MyType.exclamationPoint: 
      return '!';
  }
}

更新2:在Dart 3中,我们还有另一种方法,使用密封类:

sealed class MyType {
  @override
  String toString() {
    return switch (this) {
        Hello() => "Hello",
        World() => "world",
        Exclamation() => "!"
    };
  }
}

class Hello extends MyType {}
class World extends MyType {}
class Exclamation extends MyType {}

https://dart.dev/language/class-modifiers#sealed

旧更新:现在Dart 2.17支持在枚举上声明方法,可以比我最初的答案更干净地做到这一点:

enum MyType {
  hello,
  world,
  exclamationPoint;

  @override
  String toString() {
    switch (this) {
      case MyType.hello: 
        return 'HELLO';
      case MyType.world: 
        return 'WORLD';
      case MyType.exclamationPoint: 
        return '!';
    }
  }
}

或者你可以定义一个有三个命名构造函数的类,并重写toString方法:

class MyType {
  final String _value;

  MyType.hello(): _value = 'HELLO';
  MyType.world(): _value = 'WORLD';
  MyType.exclamationPoint(): _value = '!';

  @override
  String toString() {
    return _value;
  }
}

// Usage:
void main() {
  final hello = MyType.hello();
  final world = MyType.world();
  final punctuation = MyType.exclamationPoint();

  // Prints "HELLO, WORLD!"
  print("$hello, $world$punctuation");
}
bakd9h0s

bakd9h0s2#

在Dart中强制自定义类型的另一种方法是在构造函数中使用Assert(当使用自定义类型时)。

class SomeClass {
  final String someVariable
  SomeClass(this.someVariable) : <initializer goes here>;
}

初始化器(构造函数末尾冒号之后的部分)在构造函数本身之前执行,因此可以在那里对构造函数变量进行自定义要求。
我们使用Assert而不是异常,因为Assert抛出的错误是我们希望在生产之前在代码中捕获的错误,而不是我们希望在运行时处理的异常。
假设我们有一个名为Student的类,它有一个名为id的必填字段。id的数据类型是String,* 但 * 我们希望强制执行一个规则,即学生ID必须是24个字符的十六进制字符串。
就像你说的,TypeScript中的自然选择是使用字符串文字或template literal创建一个自定义类型,并在函数 * 和 * 中使用学生ID时将其用作参数类型。Dart不允许我们以同样的方式创建自定义数据类型(尽管有typedef类型别名),但它允许我们在尝试使用数据类型时对数据类型的 * 值 * 进行Assert。

void main() {
  
  const validId = 'a52f5a6d7b46bffe9f5ec08f';
  const invalidId = 'thisisnotavalidid';
  
  // student1 throws no errors at development time, compile time, or runtime
  final student1 = Student(id: validId, fullName: 'Jimmy Beans');
  
  // student2 throws no errors at development time, but throws an
  // AssertionError (in debug mode) at runtime when trying to 
  // instantiate Student() with an invalid id.
  final student2 = Student(id: invalidId, fullName: 'Bimmy Jeans');
  
  print(student1.toString());
}

// 24 hexadecimal characters
const String studentIdPattern = r'^[a-f\d]{24}$';

class Student {
  
  final String id;
  final String fullName;
    
  Student({
    required this.id,
    required this.fullName,
  }) : assert(RegExp(studentIdPattern, caseSensitive: false).hasMatch(id));
  
  @override
  toString() {
    return "Instance of 'Student': {id: $id, fullName: $fullName}";
  }
}

Run this code on DartPad
基本上,我们为24个字符的十六进制字符串声明了一个正则表达式模式r'^[a-f\d]{24}$'(它可以在student.dart库/文件中,也可以在一些配置文件中),并以assert()函数调用的形式向默认的Student()构造函数添加一个初始化器。
当调用Student()构造函数时,初始化器首先运行,并检查传入的id参数是否有效。
关于您的具体示例的一些附加说明:

  • 要使用正则表达式模式是r'^(HELLO)|(WORLD)$'
    *assert()调用可以在代码中的其他地方进行在这里String类型的值是什么很重要;它不必在初始化器中。
  • 默认情况下,这个方法不会在linter中抛出错误(这是Dart开发人员故意的)。确保在调试器、仿真器或测试套件中运行代码,以便触发Assert。
  • 对于更复杂的自定义类型,有一种方法可以使用Map<KeyClass, ValueClass>作为“自定义类型”,并以与TypeScript类型相当的方式使用KeyClassValueClass的枚举或类,但对于简单的模式来说,这是多余的。
lrl1mhuk

lrl1mhuk3#

是的,现在您可以使用2.13 dart版本的typedefs

typedef IntList = List<int>;
IntList il = [1, 2, 3];

https://dart.dev/language/typedefs

相关问题