MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

TypeScript中string类型的高级用法

2021-11-306.4k 阅读

字符串模板与插值

在 TypeScript 中,字符串模板是一种强大的特性,它允许我们在字符串中嵌入表达式。字符串模板使用反引号(`)来界定,而不是单引号(')或双引号(")。

例如:

let name = "Alice";
let greeting = `Hello, ${name}!`;
console.log(greeting); 

在上述代码中,${name} 就是一个插值表达式,它会被变量 name 的值所替换。我们可以在插值表达式中使用任何合法的 JavaScript 表达式,不仅仅是变量。

let num1 = 5;
let num2 = 3;
let result = `The sum of ${num1} and ${num2} is ${num1 + num2}`;
console.log(result); 

这里,${num1 + num2} 是一个加法运算的表达式,它会在字符串模板生成时被计算并插入到相应位置。

多行字符串

字符串模板还可以方便地创建多行字符串。在传统的 JavaScript 中,创建多行字符串需要使用转义字符 \n,而在字符串模板中,我们可以直接换行。

let multiLine = `This is a 
multi - line 
string.`;
console.log(multiLine); 

上述代码会输出:

This is a
multi - line
string.

标签模板

标签模板是字符串模板的一个高级应用。它允许我们通过一个函数来处理字符串模板。语法上,我们在字符串模板前加上一个函数名,这个函数就是标签函数。

function tagFunction(strings: TemplateStringsArray, ...values: any[]) {
    let result = '';
    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            result += values[i];
        }
    }
    return result;
}

let name = "Bob";
let tagged = tagFunction`Hello, ${name}!`;
console.log(tagged); 

在上述代码中,tagFunction 就是标签函数。它接收两个参数,第一个参数 strings 是一个包含字符串模板中所有静态部分的数组,第二个参数 values 是一个包含所有插值表达式值的数组。通过这种方式,我们可以对字符串模板进行自定义处理,例如字符串格式化、安全过滤等。

字符串的正则表达式操作

在 TypeScript 中,字符串与正则表达式紧密相关。我们可以使用正则表达式来进行字符串的匹配、查找、替换等操作。

匹配操作

match 方法用于在字符串中查找匹配正则表达式的内容,并返回一个数组。

let text = "The quick brown fox jumps over the lazy dog.";
let pattern = /fox/;
let result = text.match(pattern);
console.log(result); 

上述代码中,text.match(pattern) 会在 text 字符串中查找 fox,如果找到则返回一个包含匹配结果的数组,否则返回 null。这里的 pattern 是一个正则表达式字面量,我们也可以使用 RegExp 对象来创建正则表达式。

let pattern2 = new RegExp('fox');
let result2 = text.match(pattern2);
console.log(result2); 

如果正则表达式包含全局标志(g),match 方法会返回所有匹配的结果。

let text2 = "I love apples, apples are delicious.";
let pattern3 = /apples/g;
let result3 = text2.match(pattern3);
console.log(result3); 

上述代码会返回 ["apples", "apples"],因为全局匹配会找到字符串中所有符合正则表达式的内容。

查找操作

search 方法用于在字符串中查找正则表达式的匹配位置,并返回第一个匹配的索引,如果没有找到则返回 -1

let text3 = "The cat is on the mat.";
let pattern4 = /mat/;
let index = text3.search(pattern4);
console.log(index); 

这里,text3.search(pattern4) 会返回 18,表示 mat 在字符串中的起始位置。

替换操作

replace 方法用于在字符串中替换匹配正则表达式的部分。

let text4 = "The dog runs fast.";
let pattern5 = /dog/;
let newText = text4.replace(pattern5, "cat");
console.log(newText); 

上述代码会将 text4 中的 dog 替换为 cat,输出 The cat runs fast.。如果正则表达式包含全局标志(g),则会替换所有匹配的内容。

let text5 = "I have a pen, I have an apple.";
let pattern6 = /have/g;
let newText2 = text5.replace(pattern6, "had");
console.log(newText2); 

此代码会将所有的 have 替换为 had,输出 I had a pen, I had an apple.

拆分操作

split 方法可以根据正则表达式将字符串拆分成数组。

let text6 = "apple,banana,orange";
let pattern7 = /,/;
let parts = text6.split(pattern7);
console.log(parts); 

上述代码会根据逗号(, )将字符串拆分成数组 ["apple", "banana", "orange"]。我们也可以使用更复杂的正则表达式进行拆分。

let text7 = "apple, banana; orange - grape";
let pattern8 = /[,\s; -]/;
let parts2 = text7.split(pattern8);
console.log(parts2); 

这里的正则表达式 [,\s; -] 表示匹配逗号、空格、分号或短横线,所以字符串会按照这些字符进行拆分,输出 ["apple", "banana", "orange", "grape"]

字符串的遍历与迭代

在 TypeScript 中,字符串是可迭代的,这意味着我们可以使用 for...of 循环来遍历字符串的每一个字符。

let str = "Hello";
for (let char of str) {
    console.log(char);
}

上述代码会依次输出 Hello。与传统的 for 循环相比,for...of 循环更加简洁,尤其是在处理可迭代对象时。

使用 entries 方法

entries 方法返回一个新的 StringIterator 对象,该对象包含字符串中每个字符的键值对,其中键是字符的索引,值是字符本身。

let str2 = "world";
let iterator = str2.entries();
for (let [index, char] of iterator) {
    console.log(`Index: ${index}, Character: ${char}`);
}

上述代码会输出:

Index: 0, Character: w
Index: 1, Character: o
Index: 2, Character: r
Index: 3, Character: l
Index: 4, Character: d

使用 keys 方法

keys 方法返回一个新的 StringIterator 对象,该对象包含字符串中每个字符的索引。

let str3 = "hello";
let keyIterator = str3.keys();
for (let index of keyIterator) {
    console.log(`Index: ${index}`);
}

这段代码会输出字符串 hello 中每个字符的索引:01234

使用 values 方法

values 方法返回一个新的 StringIterator 对象,该对象包含字符串中的每个字符,与 for...of 循环直接遍历字符串的效果类似。

let str4 = "goodbye";
let valueIterator = str4.values();
for (let char of valueIterator) {
    console.log(`Character: ${char}`);
}

此代码会输出字符串 goodbye 中的每个字符:goodbye

字符串的类型检查与断言

在 TypeScript 中,我们可以对字符串进行类型检查和断言,以确保代码的类型安全性。

类型检查

我们可以使用 typeof 操作符来检查变量的类型是否为字符串。

let value1 = "test";
if (typeof value1 === "string") {
    console.log("It's a string.");
}

上述代码通过 typeof value1 === "string" 来检查 value1 是否为字符串类型,如果是则输出相应信息。

类型断言

类型断言用于告诉编译器某个变量的类型,即使编译器无法自动推断出该类型。在处理字符串时,我们可能会遇到需要明确指定类型的情况。

let value2: any = "example";
let strLength: number = (value2 as string).length;
console.log(strLength); 

在上述代码中,value2 的类型最初被声明为 any,通过类型断言 (value2 as string),我们告诉编译器 value2 实际上是一个字符串,这样就可以安全地访问其 length 属性。

另一种类型断言的语法是 <string>value2,但在 JSX 代码中,这种语法会与 React 的 JSX 语法冲突,所以推荐使用 as 语法。

字符串的扩展方法

TypeScript 允许我们为字符串类型添加自定义的扩展方法,这可以提高代码的复用性和可读性。

创建字符串扩展方法

我们可以通过声明一个全局的 String 接口扩展来添加自定义方法。

interface String {
    customTrim(): string;
}

String.prototype.customTrim = function (): string {
    return this.replace(/^\s+|\s+$/g, '');
};

let text8 = "   hello world   ";
let trimmed = text8.customTrim();
console.log(trimmed); 

在上述代码中,我们首先通过 interface String 声明了一个新的方法 customTrim,然后在 String.prototype 上实现了这个方法。这个方法的功能是去除字符串两端的空白字符,与原生的 trim 方法类似,但这里是我们自定义的扩展方法。

使用字符串扩展方法

一旦我们定义了字符串扩展方法,就可以像使用原生字符串方法一样使用它。

let text9 = "   some text with spaces   ";
let newText3 = text9.customTrim().toUpperCase();
console.log(newText3); 

上述代码先调用自定义的 customTrim 方法去除字符串两端的空白字符,然后调用原生的 toUpperCase 方法将字符串转换为大写并输出。

字符串与其他类型的转换

在 TypeScript 开发中,经常需要在字符串与其他类型之间进行转换。

字符串转数字

我们可以使用 Number 函数将字符串转换为数字。

let numStr1 = "123";
let num1 = Number(numStr1);
console.log(num1); 

如果字符串不能被正确解析为数字,Number 函数会返回 NaN

let numStr2 = "abc";
let num2 = Number(numStr2);
console.log(num2); 

这里 num2 的值为 NaN

我们还可以使用 parseIntparseFloat 函数进行字符串到数字的转换。parseInt 用于解析整数,parseFloat 用于解析浮点数。

let numStr3 = "45.67";
let intNum = parseInt(numStr3);
let floatNum = parseFloat(numStr3);
console.log(intNum); 
console.log(floatNum); 

上述代码中,parseInt(numStr3) 会返回 45parseFloat(numStr3) 会返回 45.67

数字转字符串

要将数字转换为字符串,可以使用 toString 方法。

let num3 = 100;
let str5 = num3.toString();
console.log(str5); 

toString 方法还可以接受一个参数,表示转换为指定进制的字符串。

let num4 = 10;
let binaryStr = num4.toString(2);
console.log(binaryStr); 

这里 num4.toString(2) 会将数字 10 转换为二进制字符串 "1010"

字符串与布尔值的转换

将字符串转换为布尔值时,空字符串 "" 会被转换为 false,非空字符串会被转换为 true

let str6 = "";
let bool1 = Boolean(str6);
console.log(bool1); 

let str7 = "not empty";
let bool2 = Boolean(str7);
console.log(bool2); 

上述代码中,bool1falsebool2true

将布尔值转换为字符串,可以使用 toString 方法。

let bool3 = true;
let str8 = bool3.toString();
console.log(str8); 

这里 bool3.toString() 会返回 "true"

字符串的国际化与本地化

在全球化的应用开发中,字符串的国际化和本地化是非常重要的。TypeScript 结合一些库可以方便地实现这些功能。

使用 Intl 对象

JavaScript 的 Intl 对象提供了语言敏感的功能,如日期、数字和字符串的格式化。对于字符串,我们可以使用 Intl.NumberFormatIntl.DateTimeFormat 等方法来实现本地化。

let num5 = 1234.56;
let numberFormatter = new Intl.NumberFormat('de - DE', {
    style: 'currency',
    currency: 'EUR'
});
let formattedNumber = numberFormatter.format(num5);
console.log(formattedNumber); 

上述代码使用 Intl.NumberFormat 将数字格式化为德国的货币格式,输出类似 1.234,56 €

字符串翻译库

为了实现字符串的国际化,我们可以使用一些专门的库,如 i18next。首先安装 i18next

npm install i18next

然后在项目中配置和使用:

import i18next from 'i18next';
import { initReactI18next } from'react - i18next';

i18next
   .use(initReactI18next)
   .init({
        resources: {
            en: {
                translation: {
                    greeting: 'Hello'
                }
            },
            fr: {
                translation: {
                    greeting: 'Bonjour'
                }
            }
        },
        lng: 'en',
        fallbackLng: 'en',
        interpolation: {
            escapeValue: false
        }
    });

let greeting = i18next.t('greeting');
console.log(greeting); 

在上述代码中,我们通过 i18next 库配置了英语和法语的翻译资源。i18next.t('greeting') 会根据当前设置的语言(这里初始化为英语)返回相应的翻译字符串。如果将 lng 设置为 fr,则会返回法语的 Bonjour

字符串在函数参数与返回值中的应用

在 TypeScript 函数中,字符串经常作为参数和返回值使用。

字符串作为函数参数

函数可以接收字符串类型的参数,并对其进行处理。

function greet(name: string) {
    return `Hello, ${name}!`;
}

let message = greet("Tom");
console.log(message); 

在上述代码中,greet 函数接收一个字符串类型的参数 name,并返回一个包含该名字的问候语。

字符串作为函数返回值

函数也可以返回字符串类型的值。

function getErrorMessage(errorCode: number): string {
    if (errorCode === 404) {
        return "Not Found";
    } else if (errorCode === 500) {
        return "Internal Server Error";
    } else {
        return "Unknown Error";
    }
}

let errorMsg = getErrorMessage(404);
console.log(errorMsg); 

这里 getErrorMessage 函数根据传入的错误码返回相应的错误信息字符串。

字符串类型的默认参数值

在函数定义中,我们可以为字符串类型的参数设置默认值。

function printMessage(message: string = "Default message") {
    console.log(message);
}

printMessage(); 
printMessage("Custom message"); 

上述代码中,printMessage 函数的 message 参数有一个默认值 "Default message"。当调用函数时如果不传入参数,就会使用默认值;如果传入参数,则使用传入的值。

字符串在面向对象编程中的应用

在 TypeScript 的面向对象编程中,字符串也有着广泛的应用。

类中的字符串属性

类可以包含字符串类型的属性。

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    greet() {
        return `Hello, I'm ${this.name}`;
    }
}

let person1 = new Person("Alice");
let greeting = person1.greet();
console.log(greeting); 

在上述 Person 类中,name 是一个字符串类型的属性,通过构造函数进行初始化,并在 greet 方法中使用。

字符串方法在类中的调用

类的方法可以调用字符串的各种方法。

class StringProcessor {
    private text: string;
    constructor(text: string) {
        this.text = text;
    }
    toUpperCaseAndTrim() {
        return this.text.trim().toUpperCase();
    }
}

let processor = new StringProcessor("   hello world   ");
let result4 = processor.toUpperCaseAndTrim();
console.log(result4); 

这里 StringProcessor 类的 toUpperCaseAndTrim 方法先调用 trim 方法去除字符串两端的空白字符,然后调用 toUpperCase 方法将字符串转换为大写。

字符串在继承中的应用

在继承关系中,字符串属性和方法也可以被继承和重写。

class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    speak() {
        return `${this.name} makes a sound.`;
    }
}

class Dog extends Animal {
    speak() {
        return `${this.name} barks.`;
    }
}

let dog1 = new Dog("Buddy");
let dogSound = dog1.speak();
console.log(dogSound); 

在上述代码中,Dog 类继承自 Animal 类,并重写了 speak 方法,使用字符串拼接返回更具体的信息。

字符串在模块与命名空间中的应用

在 TypeScript 的模块和命名空间中,字符串也会作为常量、变量等存在。

模块中的字符串常量

在一个模块中,我们可以定义字符串常量。

// constants.ts
export const API_URL = "https://example.com/api";

然后在其他模块中可以导入并使用这个常量。

// main.ts
import { API_URL } from './constants';
console.log(`API URL: ${API_URL}`); 

这里 API_URL 是一个字符串常量,在不同模块间共享。

命名空间中的字符串变量

在命名空间中,我们可以定义字符串变量。

namespace Utils {
    let message: string = "This is a utility message.";
    export function printMessage() {
        console.log(message);
    }
}

Utils.printMessage(); 

在上述 Utils 命名空间中,message 是一个字符串变量,通过 printMessage 函数可以输出该字符串。

字符串在模块导出与导入中的类型约束

当我们从模块中导出和导入字符串相关的内容时,TypeScript 的类型系统会进行类型约束。

// data.ts
export function getText(): string {
    return "Some text";
}

// app.ts
import { getText } from './data';
let text9 = getText();
console.log(text9); 

在上述代码中,getText 函数在 data.ts 中导出,其返回类型为字符串。在 app.ts 中导入并使用时,TypeScript 确保 text9 是字符串类型。

字符串在异步操作中的应用

在 TypeScript 的异步编程中,字符串也可能作为异步操作的输入或输出。

异步函数返回字符串

async function fetchData(): Promise<string> {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve("Data fetched successfully");
        }, 1000);
    });
}

fetchData().then((result) => {
    console.log(result); 
});

在上述代码中,fetchData 是一个异步函数,它返回一个 Promise,最终解析为一个字符串。

字符串作为异步函数参数

async function processText(text: string): Promise<string> {
    return new Promise((resolve) => {
        setTimeout(() => {
            let processedText = text.toUpperCase();
            resolve(processedText);
        }, 1500);
    });
}

processText("hello").then((result) => {
    console.log(result); 
});

这里 processText 函数接收一个字符串参数,并在异步操作后返回处理后的字符串。

处理异步操作中的字符串错误

async function divide(a: string, b: string): Promise<number> {
    return new Promise((resolve, reject) => {
        let numA = Number(a);
        let numB = Number(b);
        if (isNaN(numA) || isNaN(numB)) {
            reject(new Error("Invalid input, must be numbers"));
        } else if (numB === 0) {
            reject(new Error("Cannot divide by zero"));
        } else {
            let result = numA / numB;
            resolve(result);
        }
    });
}

divide("10", "2").then((result) => {
    console.log(result); 
}).catch((error) => {
    console.log(error.message); 
});

divide("abc", "2").catch((error) => {
    console.log(error.message); 
});

divide 函数中,我们处理了输入字符串不能转换为数字以及除数为零的错误情况,通过 reject 抛出包含错误信息字符串的错误对象,并在 catch 块中处理这些错误信息。