TypeScript 基础类型

发布于 2024-12-01 22:31:22 字数 18997 浏览 13 评论 0

基础类型

基础类型: BooleanNumberStringnullundefined 以及 ES6 的 Symbol 和 ES10 的 BigInt

boolean 布尔值

let isDone: boolean = false;

注意,使用构造函数 Boolean 创造的对象 不是 布尔值:

let createdBoolean: boolean = new Boolean(1)
//这样会报错 应为事实上 new Boolean() 返回的是一个 Boolean 对象

需要改成:

let createdBoolean: Boolean = new Boolean(1)
let booleand2: boolean = Boolean(1) // 可以通过函数返回布尔值

number 数字

和 JavaScript 一样,TypeScript 里的所有数字都是 浮点数

这些浮点数的类型是 number 。并支持 二进制八进制十进制十六进制

let notANumber: number = NaN;//Nan
let infinityNumber: number = Infinity;//无穷大
let decLiteral: number = 6; // 十进制
let hexLiteral: number = 0xf00d; // 十六进制
let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744; // 八进制

string 字符串

  1. 可以使用双引号( " )或单引号( ' )表示字符串。
  2. 还可以使用模版字符串,它可以定义多行文本和内嵌表达式。
let name: string = "bob";
name = "smith";
let name: string = `Gene`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.

I'll be ${ age + 1 } years old next month.`;

Null 和 Undefined

TypeScript 里, undefinednull 两者各自有自己的类型分别叫做 undefinednull

void 相似,它们的本身的类型用处不是很大:

// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;

symbol

symbol 类型的值是通过 Symbol 构造函数创建的。 传递参做为唯一标识,并且只支持 stringnumberundefined 类型的参数

let sym1 = Symbol();
let sym2 = Symbol("key"); // 可选的字符串 key
let sym3 = Symbol(1); // 可选的数字 key
let sym4 = Symbol(undefined);

symbol 是不可改变且唯一的。

let sym2 = Symbol("key");
let sym3 = Symbol("key");

sym2 === sym3; // false, symbols 是唯一的

像字符串一样,symbol 也可以被用做对象属性的键

let sym = Symbol();
let obj = {
   [sym]: "value"
};
console.log(obj[sym]); // "value"

symbol 也可以与 计算出的属性名 声明 相结合来 声明对象的属性 和 类成员


const getClassNameSymbol = Symbol();

class C {
 [getClassNameSymbol](){
   return "C";
 }
}

let c = new C();
let className = c[getClassNameSymbol](); // "C"

使用 symbol 定义的属性,是不能通过如下方式遍历拿到的

  1. for in 遍历
  2. Object.keys 遍历
  3. getOwnPropertyNames
  4. JSON.stringfy
const symbol1 = Symbol('666')
const symbol2 = Symbol('777')
const obj1= {
   [symbol1]: '小满',
   [symbol2]: '二蛋',
   age: 19,
   sex: '女'
}
// 1 for in 遍历
for (const key in obj1) {
   // 注意在 console 看 key,是不是没有遍历到 symbol1
   console.log(key)
}

// 2 Object.keys 遍历
Object.keys(obj1)
console.log(Object.keys(obj1))

// 3 getOwnPropertyNames
console.log(Object.getOwnPropertyNames(obj1))

// 4 JSON.stringfy
console.log(JSON.stringify(obj1))
怎么拿到?
  1. Object.getOwnPropertySymbols
  2. Reflect.ownKeys
// 1 拿到具体的 symbol 属性,对象中有几个就会拿到几个
Object.getOwnPropertySymbols(obj1)
console.log(Object.getOwnPropertySymbols(obj1))

// 2 es6 的 Reflect 拿到对象的所有属性
Reflect.ownKeys(obj1)
console.log(Reflect.ownKeys(obj1))

Symbol.iterator 迭代器 和 生成器 for of

支持遍历大部分类型迭代器 arrnodeListargumetnssetmap

var arr = [1,2,3,4];
let iterator:Iterator<number> = arr[Symbol.iterator]();

console.log(iterator.next());  //{ value: 1, done: false }
console.log(iterator.next());  //{ value: 2, done: false }
console.log(iterator.next());  //{ value: 3, done: false }
console.log(iterator.next());  //{ value: 4, done: false }
console.log(iterator.next());  //{ value: undefined, done: true }
interface Item {
    age: number,
    name: string
}

const array: Array<Item> = [{ age: 123, name: "1" }, { age: 123, name: "2" }, { age: 123, name: "3" }]

type mapTypes = string | number
const map:Map<mapTypes,mapTypes> = new Map()

map.set('1','王爷')
map.set('2','陆北')

const obj = {
    aaa:123,
    bbb:456
}

let set:Set<number> = new Set([1,2,3,4,5,6])
// let it:Iterator<Item> = array[Symbol.iterator]()
const gen = (erg:any): void => {
    let it: Iterator<any> = erg[Symbol.iterator]()
    let next:any= { done: false } // 默认值
    while (!next.done) {
        next =  it.next()
        if (!next.done) {
            console.log(next.value)
        }
    }
}
gen(array)

内置 symbols 用来表示语言内部的行为

1. Symbol.hasInstance

​ 方法,会被 instanceof 运算符调用。构造器对象用来识别一个对象是否是其实例。

2. Symbol.isConcatSpreadable

​ 布尔值,表示当在一个对象上调用 Array.prototype.concat 时,这个对象的数组元素是否可展开。

3. Symbol.iterator

​ 方法,被 for of 语句调用。返回对象的默认迭代器。

4. Symbol.match

​ 方法,被 String.prototype.match 调用。正则表达式用来匹配字符串。

5. Symbol.replace

​ 方法,被 String.prototype.replace 调用。正则表达式用来替换字符串中匹配的子串。

6. Symbol.search

​ 方法,被 String.prototype.search 调用。正则表达式返回被匹配部分在字符串中的索引。

7. Symbol.species

​ 函数值,为一个构造函数。用来创建派生对象。

8. Symbol.split

​ 方法,被 String.prototype.split 调用。正则表达式来用分割字符串。

9. Symbol.toPrimitive

​ 方法,被 ToPrimitive 抽象操作调用。把对象转换为相应的原始值。

10. Symbol.toStringTag

​ 方法,被内置方法 Object.prototype.toString 调用。返回创建对象时默认的字符串描述。

11. Symbol.unscopables

​ 对象,它自己拥有的属性会被 with 作用域排除在外。

其他类型

Any 任意类型 和 Unknown 顶级类型

any

为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能 来自于动态的内容 ,比如来自 用户输入第三方代码库

这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any 类型来标记这些变量(声明变量的时候没有指定任意类型也默认为 any

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

在对现有代码进行改写的时候, any 类型是十分有用的,它允许你 在编译时可选择地包含或移除类型检查

你可能认为 Object 有相似的作用,就像它在其它语言中那样。 但是 Object 类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法

let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

当你 只知道一部分数据的类型 时, any 类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:

let list: any[] = [1, true, "free"];

list[1] = 100;

unknown

TypeScript 3.0 中引入的 unknown 类型也被认为是 top type ,但它更安全。与 any 一样,所有类型都可以分配给 unknown

unknow 类型比 any 更加严格当你要使用 any 的时候可以尝试使用 unknow

//unknown 可以定义任何类型的值
let value: unknown;

value = true;             // OK
value = 42;               // OK
value = "Hello World";    // OK
value = [];               // OK
value = {};               // OK
value = null;             // OK
value = undefined;        // OK
value = Symbol("type");   // OK

any 可以作为父类型和子类型, unknow 类型只能作为父类型不能作为子类型(但可以赋值给 unknownany )

// 这样写会报错 unknow 类型只能作为父类型不能作为子类型,any 可以作为父类型和子类型
// unknown 类型不能赋值给其他类型
let names:unknown = '123'
let names2:string = names

// 这样就没问题 any 类型是可以的
let names:any = '123'
let names2:string = names   

// unknown 可赋值对象只有 unknown 和 any
let bbb:unknown = '123'
let aaa:any= '456'

aaa = bbb

any 类型在对象没有这个属性的时候还在获取是不会报错的, unknow 是不能调用属性和方法是会报错

// 如果是 any 类型在对象没有这个属性的时候还在获取是不会报错的
let obj:any = {b:1} obj.a

// 如果是 unknow 是不能调用属性和方法,会报错
let obj:unknown = {b:1,ccc:():number=>213}
obj.b
obj.ccc()

Void 空值类型

void 类型像是与 any 类型相反,它表示 没有任何类型

当一个函数没有返回值时,通常会见到其返回值类型是 void

function warnUser(): void {
  console.log("This is my warning message");
}

声明一个 void 类型的变量没有什么大用,因为只能为它赋予 undefinednull(只在--strictNullChecks 未指定时)

let unusable: void = undefined;

默认情况下 nullundefined 是所有类型的子类型。 即可以把 nullundefined 赋值给如 number 类型的变量,但 void 类型不可以分给其他类型。

//这样写会报错 void 类型不可以分给其他类型
let test: void = undefined
let num2: string = "1"

num2 = test
//这样是没问题的
let test: null = null
let num2: string = "1"

num2 = test

//或者这样的
let test: undefined = undefined
let num2: string = "1"

num2 = test

然而,当你 指定了 --strictNullChecks 标记nullundefined 只能赋值给 any 和它们各自的类型(有一个例外是 undefined 还可以赋值给 void 类型,但 null 不能)。

// tsconfig.json 开启了严格模式
{
    "compilerOptions":{
        "strict": true
    }
}

这能避免很多常见的问题。 也许在某处你想传入一个 stringnullundefined ,你可以使用联合类型 string | null | undefined

never 类型

never 类型表示的是那些永不存在的值的类型。

never 类型是那些总是会 抛出异常 或 根本就 不会有返回值函数表达式箭头函数表达式 的返回值类型。

变量也可能是 never 类型,当它们被永不为真的类型保护所约束时

注意:never 类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是 never 的子类型或可以赋值给 never 类型(除了 never 本身之外)。 即使 any 也不可以赋值给 never

下面是一些返回 never 类型的函数:

// 返回 never 的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为 never
function fail() {
    return error("Something failed");
}

// 返回 never 的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}

应用场景

interface A {
    type: "foo"
}

interface B {
    type: "bar"
}
type All = A | B ;
function handleValue(val: All) {
    switch (val.type) {
        case 'foo':
            break;
        case 'bar':
            break
        default:
            //兜底逻辑 一般是不会进入这儿如果进来了就是程序异常了
            const exhaustiveCheck:never = val;
            break
    }
}

比如新来了一个同事他新增了一个 C 接口,我们必须手动找到所有 switch 代码并处理,否则将有可能引入 BUG 。

而且这将是一个“隐蔽型”的 BUG,如果回归面不够广,很难发现此类 BUG。

那么我们就需要 TS 在类型检查阶段发现这个问题

interface A {
    type: "foo"
}

interface B {
    type: "bar"
}
interface C {
    type: "bizz"
}
type All = A | B | C;
function handleValue(val: All) {
    switch (val.type) {
        case 'foo':
            break;
        case 'bar':
            break
        default:
            //兜底逻辑 一般是不会进入这儿如果进来了就是程序异常了

            const exhaustiveCheck: never = val;
            break
    }
}

由于任何类型都不能赋值给 never 类型的变量,所以当存在进入 default 分支的可能性时,TS 的类型检查会及时帮我们发现这个问题

enum 枚举

数字枚举

使用枚举类型可以 为一组数值赋予友好的名字

enum Color {Red, Green, Blue}
let c: Color = Color.Green;

默认情况下,从 0 开始为元素编号。 也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1 开始编号:

enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;

枚举类型提供的一个便利是你可以 由枚举的值得到它的名字 。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字:

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];
console.log(colorName);  // 显示'Green'因为上面代码里它的值是 2

字符串枚举

字符串枚举的概念很简单。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。

enum Types{
   Red = 'red',
   Green = 'green',
   BLue = 'blue'
}

由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。 换句话说,如果你正在调试并且必须要读一个数字枚举的运行时的值,这个值通常是很难读的 - 它并不能表达有用的信息,字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。

异构枚举

枚举可以混合字符串和数字成员

enum Types{
   No = "No",
   Yes = 1,
}

编译为 js

var Types;
(function (Types) {
    Types["No"] = "No";
    Types[Types["Yes"] = 1] = "Yes";
})(Types || (Types = {}));

接口枚举

定义一个枚举 Types,定义一个接口 A,接口 A 有一个属性 red 值为 Types.yyds

声明对象的时候要遵循这个规则

enum Types {
  yyds,
  dddd
}
interface A {
  red:Types.yyds
}

let obj:A = {
  red:Types.yyds
}

const 常量枚举

let 和 var 都是不允许的,声明只能使用 constconst 声明的枚举会被编译成常量。

常量枚举可以提升性能(可以提升性能,计算枚举不能用 const)

enum Direction {
    Up = "Up",
    Down = "Down",
    Left = "Left",
    Right = "Right"
}

// 常量枚举
const enum Direction2 {
    Up = "Up",
    Down = "Down",
    Left = "Left",
    Right = "Right"
}
if ("Up" === Direction2.Up) {
    console.log('upupuup')
}

Array 数组

三种方式可以定义数组:

  1. 在元素类型后面接上 [] ,表示由此类型元素组成的一个数组
    let list: number[] = [1, 2, 3];
    let listAny: any[] = [1, true, 'str'];
    
    let data:number[][] = [[1,2], [3,4]];
    
  2. 使用数组泛型
    let list: Array<number> = [1, 2, 3];
    let data: Array<Array<number>> = [[1,2,3], [4,5,6]]
    
  3. 用接口表示数组 :一般用来描述类数组
    interface NumberArray {
      [index: number]: number;
    }
    let fibonacci: NumberArray = [1, 1, 2, 3, 5];
    // 表示:只要索引的类型是数字时,那么值的类型必须是数字。

arguments 类数组​

function Arr(...args:any): void {
    console.log(arguments)
    //错误的 arguments 是类数组不能这样定义
    let arr:number[] = arguments
}
Arr(111, 222, 333)


function Arr(...args:any): void {
    console.log(arguments) 
    //ts 内置对象 IArguments 定义
    let arr:IArguments = arguments
}
Arr(111, 222, 333)

//其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
interface IArguments {
  [index: number]: any;
  length: number;
  callee: Function;
}

Tuple 元组

元组就是数组的变种

元组(Tuple)是固定数量的不同类型的元素的组合的数组。

元组与集合的不同之处在于,元组中的元素类型可以是不同的,而且数量固定。元组的好处在于可以把多个元素作为一个单元传递。如果一个方法需要返回多个值,可以把这多个值作为元组返回,而不需要创建额外的类来表示。

比如定义一对值分别为 stringnumber 类型的元组。

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error

当访问一个越界的元素,会使用联合类型替代:

x[3] = 'world'; // OK, 字符串可以赋值给(string | number) 类型

console.log(x[5].toString()); // OK, 'string' 和 'number' 都有 toString

x[6] = true; // Error, 布尔不是(string | number) 类型

Object

object 表示非原始类型,也就是除 numberstringbooleansymbolnullundefined 之外的类型。

使用 object 类型,就可以更好的表示像 Object.create 这样的 API。例如:

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

类型断言(即类型转换)

有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。

通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换 ,但是不进行特殊的数据检查和解构。 它没有运行时的影响, 只是在编译阶段起作用 。 TypeScript 会假设你,程序员,已经进行了必须的检查。

类型断言有两种形式(两种形式是等价的):

  1. 尖括号语法
    let someValue: any = "this is a string";
    let strLength: number = (<string>someValue).length;
    
  1. as 语法 (在 TypeScript 里 使用 JSX 时 ,只有 as 语法断言是被允许):值 as 类型 value as string
    let someValue: any = "this is a string";
    let strLength: number = (someValue as string).length;
    

注意: 「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误

使用 any 临时断言

window.abc = 123; // 这样写会报错因为 window 没有 abc 这个东西

(window as any).abc = 123 // 可以使用 any 临时断言在 any 类型的变量上,访问任何属性都是允许的。

as const

as 是对字面值的 断言 ,与 const 直接定义常量是有区别的

const names = '小满'
names = 'aa' //无法修改

let names2 = '小满' as const // 跟直接 const 声明是一样的
names2 = 'aa' //无法修改
// 数组
let a1 = [10, 20] as const;
const a2 = [10, 20];

a1.unshift(30); // 错误,此时已经断言字面量为[10, 20],数据无法做任何修改
a2.unshift(30); // 通过,没有修改指针

类型断言是不具影响力的

在下面的例子中,将 something 断言为 boolean 虽然可以通过编译,但是并没有什么用 并不会影响结果,因为编译过程中会删除类型断言

function toBoolean(something: any): boolean {
    return something as boolean;
}

toBoolean(1);
// 返回值为 1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

皇甫轩

暂无简介

文章
评论
727 人气
更多

推荐作者

迎风吟唱

文章 0 评论 0

qq_hXErI

文章 0 评论 0

茶底世界

文章 0 评论 0

捎一片雪花

文章 0 评论 0

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文