如何在TypeScript中处理日期字符串

目录
  • 前言:
  • 一、模板字面量类型
  • 二、类型谓词缩小范围
  • 三、定义日期字符串
  • 总结:

前言:

在我最近的一个项目中,我必须去处理多个自定义的日期字符串表示法,比如YYYY-MM-DDYYYYMMDD。由于这些日期是字符串变量,TypeScript默认会推断成为string类型。虽然这在技术实现上没有错,但是在工作中使用这样的类型定义是很宽泛的,使得有效处理这些日期字符串变得很困难。例如,let dog = 'alfie'也被推断为一个string类型。

在这篇文章中,我会将我的解决方法呈现给你,通过输入这些日期字符串来改善开发者的体验并减少潜在的错误。

在进入编码之前,让我们简单回顾下实现目标需求要利用到的typescript特性,即模板字面量类型和通过类型谓词缩小范围。

一、模板字面量类型

在typescript4.1版本中引入,模板字面量类型和JavaScript的模板字符串语法相同,但是是作为类型使用。模板字面量类型解析为一个给定模板的所有字符串组合的联合。这听起来可能有点抽象,

直接看代码:

type Person = 'Jeff' | 'Maria'
type Greeting = `hi ${Person}!` // Template literal type
const validGreeting: Greeting = `hi Jeff!` //
// note that the type of `validGreeting` is the union `"hi Jeff!" | "hi Maria!`
const invalidGreeting: Greeting = `bye Jeff!` //
// Type '"bye Jeff!"' is not assignable to type '"hi Jeff!" | "hi Maria!"

模板字面量类型非常强大,允许你对这些类型进行通用类型操作。例如,大写字母化。

type Person = 'Jeff' | 'Maria'
type Greeting = `hi ${Person}!`
type LoudGreeting = Uppercase<Greeting> // Capitalization of template literal type
const validGreeting: LoudGreeting = `HI JEFF!` //
const invalidGreeting: LoudGreeting = `hi jeff!` //
// Type '"hi Jeff!"' is not assignable to type '"HI JEFF!" | "HI MARIA!"

二、类型谓词缩小范围

typescript在缩小类型范围方面表现得非常好,可以看下面这个例子:

let age: string | number = getAge();
// `age` is of type `string` | `number`
if (typeof age === 'number') {
  // `age` is narrowed to type `number`
} else {
  // `age` is narrowed to type `string`
}

也就是说,在处理自定义类型时,告诉typescript编译器如何进行类型缩小是有帮助的。例如,当我们想在执行运行时验证后缩小到一个类型时,在这种情况下,类型谓词窄化,或者用户定义的类型守护,就可以派上用场。

在下面这个例子中,isDog类型守护通过检查类型属性来帮助缩小animal变量的类型:

type Dog = { type: 'dog' };
type Horse = { type: 'horse' };
//  custom type guard, `pet is Dog` is the type predicate
function isDog(pet: Dog | Horse): pet is Dog {
  return pet.type === 'dog';
}
let animal: Dog | Horse = getAnimal();
// `animal` is of type `Dog` | `Horse`
if (isDog(animal)) {
  // `animal` is narrowed to type `Dog`
} else {
  // `animal` is narrowed to type `Horse`
}

三、定义日期字符串

为了简洁起见,这个例子只包含YYYYMMDD日期字符串的代码。

首先,我们需要定义模板字面量类型来表示所有类似日期的字符串的联合类型

type oneToNine = 1|2|3|4|5|6|7|8|9
type zeroToNine = 0|1|2|3|4|5|6|7|8|9
/**
 * Years
 */
type YYYY = `19${zeroToNine}${zeroToNine}` | `20${zeroToNine}${zeroToNine}`
/**
 * Months
 */
type MM = `0${oneToNine}` | `1${0|1|2}`
/**
 * Days
 */
type DD = `${0}${oneToNine}` | `${1|2}${zeroToNine}` | `3${0|1}`
/**
 * YYYYMMDD
 */
type RawDateString = `${YYYY}${MM}${DD}`;
const date: RawDateString = '19990223' //
const dateInvalid: RawDateString = '19990231' //31st of February is not a valid date, but the template literal doesnt know!
const dateWrong: RawDateString = '19990299'//  Type error, 99 is not a valid day

从上面的例子可以得知,模板字面量类型有助于指定日期字符串的格式,但是没有对这些日期进行实际验证。因此,编译器将19990231标记为一个有效的日期,即使它是不正确的,只因为它符合模板的类型。

另外,当检查上面的变量如datedateInvaliddateWrong时,你会发现编辑器会显示这些模板字面的所有有效字符的联合。虽然很有用,但是我更喜欢设置名义类型,使得有效的日期字符串的类型是DateString,而不是"19000101" | "19000102" | "19000103" | ...。在添加用户定义的类型保护时,名义类型也会派上用场。

type Brand<K, T> = K & { __brand: T };
type DateString = Brand<RawDateString, 'DateString'>;
const aDate: DateString = '19990101'; //
// Type 'string' is not assignable to type 'DateString'

为了确保我们的DateString类型也代表有效的日期,我们将设置一个用户定义的类型保护来验证日期和缩小类型

/**
 * Use `moment`, `luxon` or other date library
 */
const isValidDate = (str: string): boolean => {
  // ...
};
//User-defined type guard
function isValidDateString(str: string): str is DateString {
  return str.match(/^\d{4}\d{2}\d{2}$/) !== null && isValidDate(str);
}

现在,让我们看看几个例子中的日期字符串类型。在下面的代码片段中,用户定义的类型保护被应用于类型缩小,允许TypeScript编译器将类型细化为比声明的更具体的类型。然后,在一个工厂函数中应用了类型保护,以从一个未标准化的输入字符串中创建一个有效的日期字符串。

/**
 *   Usage in type narrowing
 */
// valid string format, valid date
const date: string = '19990223';
if (isValidDateString(date)) {
  // evaluates to true, `date` is narrowed to type `DateString`
}
//  valid string format, invalid date (February doenst have 31 days)
const dateWrong: string = '19990231';
if (isValidDateString(dateWrong)) {
  // evaluates to false, `dateWrong` is not a valid date, even if its shape is YYYYMMDD
}

/**
 *   Usage in factory function
 */
function toDateString(str: RawDateString): DateString {
  if (isValidDateString(str)) return str;
  throw new Error(`Invalid date string: ${str}`);
}
//  valid string format, valid date
const date1 = toDateString('19990211');
// `date1`, is of type `DateString`
//  invalid string format
const date2 = toDateString('asdf');
//  Type error: Argument of type '"asdf"' is not assignable to parameter of type '"19000101" | ...
//  valid string format, invalid date (February doenst have 31 days)
const date3 = toDateString('19990231');
//  Throws Error: Invalid date string: 19990231

总结:

我希望这篇文章能让我们了解TypeScript在输入自定义字符串方面的能力。请记住,这种方法也适用于其他自定义字符串,如自定义user-idsuser-xxxx,以及其他日期字符串,像YYYY-MM-DD

当结合用户定义的类型防护、模板字面字符串和名义类型时,可能性是无穷的。

到此这篇关于如何在TypeScript中处理日期字符串的文章就介绍到这了,更多相关TypeScript处理字符串内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • TypeScript 引用资源文件后提示找不到的异常处理技巧

    在tsx中引用图片,在文件文本编辑器中提示错误引用: typescript无法识别非代码文件(js是可以的).如果需要在ts中识别此文件资源,可以先声明文件类型. 新建一个ts文件,比如global.d.ts(.d.ts是typescript declaration file的简称),并放在主要代码文件夹下. 在ts文件中,添加各种文件类型的声明,比如: declare module '*.svg' { interface Svg { content: string; id: string; v

  • Vue+TypeScript中处理computed方式

    目录 什么是 「computed」 「computed」 的用途 在TypeScript怎么用 另一种方案 vue computed正确使用方式 computed or methods computed or watch 什么是 「computed」 「computed」 是Vue中提供的一个计算属性.它被混入到Vue实例中,所有的getter和setter的this上下文自动的绑定为Vue实例.计算属性的结果会被缓存,除非依赖的响应式property变化才会从新计算. 「computed」 的

  • 如何在TypeScript中处理日期字符串

    目录 前言: 一.模板字面量类型 二.类型谓词缩小范围 三.定义日期字符串 总结: 前言: 在我最近的一个项目中,我必须去处理多个自定义的日期字符串表示法,比如YYYY-MM-DD和YYYYMMDD.由于这些日期是字符串变量,TypeScript默认会推断成为string类型.虽然这在技术实现上没有错,但是在工作中使用这样的类型定义是很宽泛的,使得有效处理这些日期字符串变得很困难.例如,let dog = 'alfie'也被推断为一个string类型. 在这篇文章中,我会将我的解决方法呈现给你,

  • 如何在TypeScript中正确的遍历一个对象

    目录 JavaScript TypeScript for...in Object.keys Object.entries 思考 总结 JavaScript 在讲解用 Ts 遍历一个对象之前, 我们先说说 在 Js 中怎么实现, for...in.Object.keys, 一个简单的例子: // for...in const obj = { name: 'itsuki', address: 'hangzhou', }; for (const key in obj) { console.log(ke

  • 在Typescript中如何使用for...in详解

    如何在Typescript中使用for...in ?本人在TS中用for...in出现了些问题,也想到了一些解决方法.那么先来看看下面报错的代码吧. interface ABC { a: string b: string } const x: ABC = { a:'1', b:'2' } const y: ABC = { a:'3', b:'4' } for (const key in x) { // 在类型 "ABC" 上找不到具有类型为 "string" 的参数

  • TypeScript中import type与import的区别详析

    目录 背景 import type vs import 使用 import type 的好处 参考链接 总结 背景 这周遇到了一个比较奇怪的问题:如何在 TypeScript 中根据某个 enum 的取值来执行后续逻辑? 按理来说应该很简单,这是 enum 的定义: export enum MyEnum { DEFAULT = 0, SOME_VALUE = 1, SOME_OTHER_VALUE = 2, } 然后在另一个项目中,通过 import type 来引入: import type

  • TypeScript中定义变量方式以及数据类型详解

    目录 TypeScript定义变量 变量声明格式 变量类型推导 JS和TS的数据类型 TS中使用JS的数据类型 number类型 boolean类型 string类型 Array类型 Object类型 Symbol类型 null和undefined类型 TS自身特有的数据类型 any类型 unknown类型 void类型 never类型 tuple类型 总结 TypeScript定义变量 变量声明格式 我在前面强调过,在TypeScript中定义变量需要指定 标识符 的类型. 所以完整的声明格式

  • 如何在JavaScript中创建具有多个空格的字符串?

    通过创建变量 var a = 'something' + '                         ' + 'something' 我得到这个值:'something something'. 如何在JavaScript中创建一个包含多个空格的字符串? 使用\xa0- 它是一个NO-BREAK SPACE char. 从UTF-8编码表和Unicode字符引用,可以写成如下: var a = 'something' + '\xa0\xa0\xa0\xa0\xa0\xa0\xa0' + '

  • 关于mysql中时间日期类型和字符串类型的选择

    目录 一.DATETIME.TIMESTAMP 的用法 1.相同点 2.不同点 3.选择 二.varchar 和 text 数据类型的用法 1.相同点 2.不同点 3.选择 一.DATETIME.TIMESTAMP 的用法 MySQL中有多种表示时间日期的数据类型,主要有YEAR.TIME.DATE.DATETIME.TIMESTAMP等 1.相同点 datetime和timestamp都可以表示 YYYY-MM-DDHH:MM:SS 这种年月日时分秒格式的数据. 2.不同点 datetime存

  • 如何在Python 中使用 join() 函数把列表拼接成一个字符串

    目录 1.设置 end=’’ 2.拼接字符串(string) 3.举例 内容概要:如何把列表中的元素拼接为一个字符串呢?本文介绍了采用 join() 函数的解决方法. 问题:有一个列表,比如:letters=[‘a’,‘b’,‘c’],想要把列表的元素连续显示出来,应该怎么办? 解决办法: 1.设置 end=’’ 如何使Python中的print()语句运行结果不换行 的方法,设置 end=’’. 如下代码所示: letters=['a','b','c'] for letter in lette

  • 如何在Python 中使用 join() 函数把列表拼接成一个字符串

    目录 1.设置 end=’’ 2.拼接字符串(string) 3.举例 内容概要:如何把列表中的元素拼接为一个字符串呢?本文介绍了采用 join() 函数的解决方法. 问题:有一个列表,比如:letters=[‘a’,‘b’,‘c’],想要把列表的元素连续显示出来,应该怎么办? 解决办法: 1.设置 end=’’ 如何使Python中的print()语句运行结果不换行 的方法,设置 end=’’. 如下代码所示: letters=['a','b','c'] for letter in lette

  • 如何在c++中实现字符串分割函数split详解

    前言 在学习c++中string相关基本用法的时候,发现了sstream的istringstream[1]可以将字符串类似于控制台的方式进行输入,而实质上这个行为等同于利用空格将一个字符串进行了分割,于是考虑到可以利用这个特性来实现c++库函数中没有的字符串分割函数split string src("Avatar 123 5.2 Titanic K"); istringstream istrStream(src); //建立src到istrStream的联系 string s1, s2

随机推荐

其他