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

TypeScript中number类型的全面解析

2021-02-183.5k 阅读

number 类型基础概述

在 TypeScript 中,number 类型用于表示所有的数字,无论是整数还是浮点数。这与 JavaScript 中的数值类型一致,JavaScript 内部使用 IEEE 754 格式来存储数字,TypeScript 基于 JavaScript,number 类型也遵循相同的存储规则。

基本数值表示

  • 整数表示:最常见的整数写法就是直接写数字,例如:
let myInt: number = 42;

这里我们声明了一个变量 myInt,类型为 number 并赋值为 42。

  • 浮点数表示:浮点数可以通过小数点来表示,如:
let myFloat: number = 3.14;

也可以使用科学计数法,对于很大或很小的数字非常有用。例如:

let largeNumber: number = 1.23e5; // 1.23 * 10^5,即 123000
let smallNumber: number = 4.56e-3; // 4.56 * 10^-3,即 0.00456

数值运算

TypeScript 中的 number 类型支持一系列的基本算术运算,这些运算与数学中的常规运算类似。

加法运算

加法运算使用 + 运算符,它可以用于整数和浮点数的相加。

let num1: number = 5;
let num2: number = 3;
let sum: number = num1 + num2;
console.log(sum); // 输出 8

当一个整数与一个浮点数相加时,结果为浮点数:

let intNum: number = 10;
let floatNum: number = 2.5;
let result: number = intNum + floatNum;
console.log(result); // 输出 12.5

减法运算

减法运算使用 - 运算符。

let minuend: number = 10;
let subtrahend: number = 4;
let difference: number = minuend - subtrahend;
console.log(difference); // 输出 6

乘法运算

乘法运算通过 * 运算符实现。

let factor1: number = 3;
let factor2: number = 7;
let product: number = factor1 * factor2;
console.log(product); // 输出 21

除法运算

除法运算使用 / 运算符,结果通常为浮点数,即使两个操作数都是整数。

let dividend: number = 15;
let divisor: number = 3;
let quotient: number = dividend / divisor;
console.log(quotient); // 输出 5

如果除数为 0,在 JavaScript 和 TypeScript 中会得到 Infinity-Infinity,取决于被除数的符号。

let positiveDividend: number = 10;
let zeroDivisor: number = 0;
let positiveResult: number = positiveDividend / zeroDivisor;
console.log(positiveResult); // 输出 Infinity

let negativeDividend: number = -10;
let negativeResult: number = negativeDividend / zeroDivisor;
console.log(negativeResult); // 输出 -Infinity

取模运算

取模运算(求余数)使用 % 运算符。

let dividendMod: number = 17;
let divisorMod: number = 5;
let remainder: number = dividendMod % divisorMod;
console.log(remainder); // 输出 2

特殊数值

number 类型中,有几个特殊的数值需要特别关注。

NaN

NaN 代表 “Not a Number”,通常在数学运算无法得出有意义的结果时出现,比如 0 / 0 或者对非数字字符串进行数学运算。

let resultNaN1: number = 0 / 0;
console.log(resultNaN1); // 输出 NaN

let resultNaN2: number = Number('abc');
console.log(resultNaN2); // 输出 NaN

需要注意的是,NaN 与任何值(包括它自身)进行比较都返回 false

let nanValue: number = NaN;
console.log(nanValue === NaN); // 输出 false

要判断一个值是否为 NaN,可以使用 isNaN 函数。

let value1: number = NaN;
let value2: number = 10;
console.log(isNaN(value1)); // 输出 true
console.log(isNaN(value2)); // 输出 false

Infinity 和 -Infinity

Infinity 表示正无穷大,-Infinity 表示负无穷大。当一个正数除以 0 时会得到 Infinity,负数除以 0 时会得到 -Infinity

let positiveInfinity: number = 1 / 0;
console.log(positiveInfinity); // 输出 Infinity

let negativeInfinity: number = -1 / 0;
console.log(negativeInfinity); // 输出 -Infinity

Infinity-Infinity 也遵循一些数学规则,例如任何数与 Infinity 相加仍为 Infinity(除了 -InfinityInfinity 相加结果为 NaN)。

let num1Inf: number = 10;
let infValue: number = Infinity;
let sumInf: number = num1Inf + infValue;
console.log(sumInf); // 输出 Infinity

类型转换

在 TypeScript 编程中,经常需要进行数值类型的转换,包括从其他类型转换为 number 类型,以及 number 类型转换为其他类型。

其他类型转换为 number 类型

  • 字符串转换为 number:可以使用 Number 函数或者 parseIntparseFloat 函数。
    • 使用 Number 函数:
let str1: string = '123';
let numFromStr1: number = Number(str1);
console.log(numFromStr1); // 输出 123

let str2: string = '4.56';
let numFromStr2: number = Number(str2);
console.log(numFromStr2); // 输出 4.56

let str3: string = 'abc';
let numFromStr3: number = Number(str3);
console.log(numFromStr3); // 输出 NaN
  • 使用 parseIntparseInt 用于将字符串解析为整数,它会忽略字符串开头的空格,并从第一个非空格字符开始解析,直到遇到非数字字符。
let strInt1: string = '  456';
let intFromStr1: number = parseInt(strInt1);
console.log(intFromStr1); // 输出 456

let strInt2: string = '789abc';
let intFromStr2: number = parseInt(strInt2);
console.log(intFromStr2); // 输出 789
  • 使用 parseFloatparseFloat 用于将字符串解析为浮点数,同样会忽略开头的空格,从第一个非空格字符开始解析,直到遇到无法解析为浮点数的字符。
let strFloat1: string = '  3.14';
let floatFromStr1: number = parseFloat(strFloat1);
console.log(floatFromStr1); // 输出 3.14

let strFloat2: string = '5.67xyz';
let floatFromStr2: number = parseFloat(strFloat2);
console.log(floatFromStr2); // 输出 5.67
  • 布尔值转换为 numbertrue 转换为 1false 转换为 0
let boolTrue: boolean = true;
let numFromTrue: number = Number(boolTrue);
console.log(numFromTrue); // 输出 1

let boolFalse: boolean = false;
let numFromFalse: number = Number(boolFalse);
console.log(numFromFalse); // 输出 0

number 类型转换为其他类型

  • number 转换为字符串:可以使用 toString 方法或者 String 函数。
    • 使用 toString 方法:
let numToStr1: number = 123;
let strFromNum1: string = numToStr1.toString();
console.log(strFromNum1); // 输出 '123'

let numToStr2: number = 4.56;
let strFromNum2: string = numToStr2.toString();
console.log(strFromNum2); // 输出 '4.56'
  • 使用 String 函数:
let numToStr3: number = 789;
let strFromNum3: string = String(numToStr3);
console.log(strFromNum3); // 输出 '789'
  • number 转换为布尔值0 转换为 false,非 0 值转换为 true
let numToBool1: number = 0;
let boolFromNum1: boolean = Boolean(numToBool1);
console.log(boolFromNum1); // 输出 false

let numToBool2: number = 5;
let boolFromNum2: boolean = Boolean(numToBool2);
console.log(boolFromNum2); // 输出 true

在函数中的应用

number 类型在函数的参数和返回值中经常使用。

接受 number 类型参数的函数

function addNumbers(a: number, b: number): number {
    return a + b;
}

let resultAdd: number = addNumbers(3, 5);
console.log(resultAdd); // 输出 8

在这个例子中,addNumbers 函数接受两个 number 类型的参数 ab,并返回它们的和,返回值类型也是 number

返回 number 类型值的函数

function calculateArea(radius: number): number {
    return Math.PI * radius * radius;
}

let circleArea: number = calculateArea(4);
console.log(circleArea); // 输出约 50.26548245743669

calculateArea 函数接受一个表示圆半径的 number 类型参数 radius,并返回圆的面积,返回值类型为 number

在数组和对象中的应用

包含 number 类型元素的数组

可以创建一个数组,其元素类型为 number

let numbersArray: number[] = [1, 2, 3, 4, 5];
console.log(numbersArray[2]); // 输出 3

这里我们定义了一个 numbersArray 数组,它只能包含 number 类型的元素。

对象中的 number 类型属性

interface Point {
    x: number;
    y: number;
}

let myPoint: Point = {
    x: 10,
    y: 20
};
console.log(myPoint.x); // 输出 10
console.log(myPoint.y); // 输出 20

在这个例子中,我们定义了一个接口 Point,它有两个 number 类型的属性 xy。然后创建了一个 myPoint 对象,该对象符合 Point 接口的定义。

类型检查与断言

在 TypeScript 中,可以对 number 类型进行类型检查,并使用类型断言来更明确地指定类型。

类型检查

可以使用 typeof 操作符来检查一个值的类型是否为 number

let value1Check: number = 10;
let value2Check: string = 'abc';

console.log(typeof value1Check === 'number'); // 输出 true
console.log(typeof value2Check === 'number'); // 输出 false

类型断言

有时候,TypeScript 可能无法准确推断出一个值的类型,这时可以使用类型断言。

let someValue: any = '123';
let numValue: number = <number>someValue;
console.log(numValue); // 输出 123(这里假设 someValue 实际可以转换为 number)

或者使用更现代的语法:

let someValue2: any = '456';
let numValue2: number = someValue2 as number;
console.log(numValue2); // 输出 456(这里假设 someValue2 实际可以转换为 number)

精度问题

由于 JavaScript 使用 IEEE 754 格式存储数字,在处理浮点数时可能会出现精度问题。

let num1Precision: number = 0.1;
let num2Precision: number = 0.2;
let sumPrecision: number = num1Precision + num2Precision;
console.log(sumPrecision); // 输出 0.30000000000000004

这是因为 0.1 和 0.2 在二进制中无法精确表示,导致相加的结果有微小的偏差。在进行浮点数比较或涉及精确计算时,需要特别小心。一种解决方法是使用专门的库,如 decimal.js,它可以提供高精度的十进制运算。

位运算

TypeScript 支持一系列的位运算,这些运算在处理整数的二进制表示时非常有用。位运算直接对数字的二进制位进行操作。

按位与(&)

按位与运算符 & 对两个数字的二进制位进行与操作。只有当两个对应的二进制位都为 1 时,结果位才为 1,否则为 0。

let num1Bitwise: number = 5; // 二进制表示为 0101
let num2Bitwise: number = 3; // 二进制表示为 0011
let resultAnd: number = num1Bitwise & num2Bitwise;
console.log(resultAnd); // 二进制结果为 0001,输出 1

按位或(|)

按位或运算符 | 对两个数字的二进制位进行或操作。只要两个对应的二进制位中有一个为 1,结果位就为 1,只有当两个都为 0 时,结果位才为 0。

let num3Bitwise: number = 5; // 二进制表示为 0101
let num4Bitwise: number = 3; // 二进制表示为 0011
let resultOr: number = num3Bitwise | num4Bitwise;
console.log(resultOr); // 二进制结果为 0111,输出 7

按位异或(^)

按位异或运算符 ^ 对两个数字的二进制位进行异或操作。当两个对应的二进制位不同时,结果位为 1,相同时为 0。

let num5Bitwise: number = 5; // 二进制表示为 0101
let num6Bitwise: number = 3; // 二进制表示为 0011
let resultXor: number = num5Bitwise ^ num6Bitwise;
console.log(resultXor); // 二进制结果为 0110,输出 6

按位非(~)

按位非运算符 ~ 对一个数字的二进制位进行取反操作。它将 0 变为 1,1 变为 0。

let num7Bitwise: number = 5; // 二进制表示为 0101
let resultNot: number = ~num7Bitwise;
console.log(resultNot); // 二进制结果为 1010,在有符号整数表示中为 -6

左移(<<)

左移运算符 << 将一个数字的二进制位向左移动指定的位数。右侧空出的位用 0 填充。

let num8Bitwise: number = 5; // 二进制表示为 0101
let resultLeftShift: number = num8Bitwise << 2;
console.log(resultLeftShift); // 二进制结果为 010100,输出 20

右移(>>)

右移运算符 >> 将一个数字的二进制位向右移动指定的位数。对于有符号整数,左侧空出的位用符号位(即最高位)填充。

let num9Bitwise: number = 20; // 二进制表示为 010100
let resultRightShift: number = num9Bitwise >> 2;
console.log(resultRightShift); // 二进制结果为 000101,输出 5

无符号右移(>>>)

无符号右移运算符 >>> 将一个数字的二进制位向右移动指定的位数,左侧空出的位用 0 填充,不考虑符号位。

let num10Bitwise: number = -5; // 二进制表示为 11111111111111111111111111111011
let resultUnsignedRightShift: number = num10Bitwise >>> 2;
console.log(resultUnsignedRightShift); // 二进制结果为 00111111111111111111111111111110,输出 1073741822

与其他类型的兼容性

在 TypeScript 中,number 类型与其他类型之间存在一定的兼容性规则。

与 any 类型的兼容性

number 类型的值可以赋值给 any 类型的变量,反之亦然。

let anyValue: any = 10;
let numFromAny: number = anyValue;

let numValue: number = 20;
let anyFromNum: any = numValue;

与 unknown 类型的兼容性

number 类型的值不能直接赋值给 unknown 类型的变量,需要进行类型检查或断言。

let unknownValue: unknown;
// let numToUnknown: unknown = 10; // 错误,不能直接赋值
if (typeof 10 === 'number') {
    unknownValue = 10;
}

let unknownToNum: number;
// unknownToNum = unknownValue; // 错误,不能直接赋值
if (typeof unknownValue === 'number') {
    unknownToNum = unknownValue;
}

与 never 类型的兼容性

number 类型与 never 类型不兼容,never 类型表示永远不会出现的值,而 number 类型是实际存在的数值类型。

let numValue2: number;
// numValue2 = neverValue; // 错误,不兼容

在泛型中的应用

在泛型编程中,number 类型可以作为类型参数或约束类型参数的范围。

作为类型参数

function identity<T>(arg: T): T {
    return arg;
}

let numIdentity: number = identity<number>(10);
console.log(numIdentity); // 输出 10

在这个 identity 函数中,我们使用 number 作为类型参数 T,这样函数就可以接受并返回 number 类型的值。

作为类型参数的约束

function add<T extends number>(a: T, b: T): T {
    return a + b as T;
}

let sumGeneric: number = add<number>(5, 3);
console.log(sumGeneric); // 输出 8

add 函数中,我们通过 T extends number 约束类型参数 T 必须是 number 类型,这样函数只能接受 number 类型的参数并返回 number 类型的值。

在类中的应用

类的属性和方法使用 number 类型

class Circle {
    private radius: number;

    constructor(radius: number) {
        this.radius = radius;
    }

    calculateArea(): number {
        return Math.PI * this.radius * this.radius;
    }
}

let myCircle: Circle = new Circle(5);
let circleArea: number = myCircle.calculateArea();
console.log(circleArea); // 输出约 78.53981633974483

Circle 类中,radius 属性是 number 类型,calculateArea 方法接受 number 类型的参数并返回 number 类型的面积值。

在模块中的应用

在 TypeScript 模块中,number 类型同样广泛应用于导出和导入的函数、变量等。

导出包含 number 类型的函数和变量

// mathUtils.ts
export function multiply(a: number, b: number): number {
    return a * b;
}

export const PI: number = 3.14159;

导入并使用包含 number 类型的函数和变量

// main.ts
import { multiply, PI } from './mathUtils';

let resultMultiply: number = multiply(4, 5);
console.log(resultMultiply); // 输出 20

let circleCircumference: number = 2 * PI * 3;
console.log(circleCircumference); // 输出约 18.84954

最佳实践

  1. 明确类型声明:在定义变量、函数参数和返回值时,始终明确指定 number 类型,这样可以利用 TypeScript 的类型检查功能,减少运行时错误。
  2. 处理精度问题:在涉及浮点数运算时,要注意精度问题。如果需要精确计算,考虑使用专门的高精度库,如 decimal.js
  3. 类型转换注意事项:在进行类型转换时,确保目标类型是合理的,并且对可能出现的转换失败情况进行处理,例如 NaN 的情况。
  4. 使用类型断言谨慎:虽然类型断言很有用,但要谨慎使用,避免过度使用导致类型安全问题。只有在确实知道值的实际类型时才使用断言。

通过深入理解和合理应用 TypeScript 中的 number 类型,开发者可以编写出更健壮、可靠的代码,充分发挥 TypeScript 在类型安全方面的优势。无论是简单的数值运算,还是复杂的应用逻辑,number 类型都是编程中不可或缺的一部分。在实际项目中,结合项目需求和 number 类型的各种特性,能够更好地实现业务逻辑和优化代码结构。同时,随着项目规模的扩大,对 number 类型的准确使用和管理将有助于提高代码的可维护性和可扩展性。例如,在大型数据处理项目中,对数值的精度控制、类型检查以及合理的运算逻辑设计,都直接关系到项目的正确性和性能。在图形绘制、游戏开发等涉及到坐标计算、物理模拟等场景中,number 类型的准确运用更是至关重要。总之,掌握 number 类型的全面知识,是成为优秀 TypeScript 开发者的重要一步。