Ts基础学习笔记

它们的定义文件同样在 TypeScript 核心# TS笔记基础

1.原始数据类型

JavaScript的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。

原始数据类型包括:字符串数字布尔值nullundefind 已经es6新增的symbol 和es10新增的bigInt

布尔值

let isDone:boolean = false;
// 编译通过
// 后面约定,未强调编译错误的代码片段,一律未编译通过

注意,使用构造函数创建的boolean 不是布尔值

let isDone:boolean = nwe Boolean(1);
// Type 'Boolean' is not assignable to type 'boolean'.
// boolean' is a primitive, but 'Boolean' is a wrapper object. 
// Prefer using 'boolean' when possible.

使用Boolean创建的不是一个布尔值,它实际上是一个boolean 对象。

在TypeScript里,boolean 表示基本数据类型,而Boolean 则是JavaScript的一个构造函数。其他基本类型一样(除了undefinednull)。

数值

使用number定义类型

let decLiteral:number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二进制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八进制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;

字符串

使用string定义类型

let myName:string = '小明';
leg age:string = '18';
let my:string = `我的名字${
      
      age}`;

空值

JavaScript中没有空值(void)的概念, 在 TypeScript 中 ,一般空值用于函数的返回.

function alertName():void{
    
    
    alart('我是图图小可爱')
   return;
    // return 不能返回结果,
}

void函数返回或者不返回,实际上在空值也是一种返回,返回空

Undefind和Null

使用undefindnull定义类型

let u:undefind = undefind;
let n:null = null;

void的区别是undefindnull是所有类型的子类型。也就是说undefindnull可以赋值所有的已定义类型变量

let s:string = undefind;
let s1:string = null;
let num:number = undefind;
let num1:number = null;

void 类型的变量不能赋值给 number 类型的变量

let u:void;
let num:number = u;
// Type 'void' is not assignable to type 'number'.

2.任意值

任意值(any)表示允许赋值成任意的值。一般不会用。

什么是任意值

let s1:string = 'seven';
s1 = 10;
//  index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'

不允许从字符串改变成数字,但如果是any类型,则 允许被赋值为任意类型

let s1:any = 'seven';
s1 = 10

任意值的属性和方法

在任意值上访问任意属性都是允许的

let anyTing:any = 'hello';
console.log(anyTing.myName);
console.log(anyTing.firstName)

在任意值上访问任意方法都是允许的

let anyTing:any = 'Tom';
anyTing.setName('Jeary');
anyTing.setName('Jeary').setHello();

可以认为,声明一个任意值之后,对它的如何操作,返回的内容的类型都是任意值 。

未声明类型

let someTing;
someTing = 10;
someTing = '你好';

等价于

let someTing:any;
someTing = 10;
someTing = '你好';

变量声明未赋值类型,默认是任意值的类型。

3.类型推论

如果没有明确指定类型,那么TypeScript会根据类型推论来的规则来推断出一个类型

什么是类型推论

let myFavoriteNumber  = 'seven';
myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

实际上,它会等价于:

let myFavoriteNumber:string = 'seven';
myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。

如果声明没定义类型,不管之后有没有赋值,它都会被推断为any类型

let someTing;
somgTing = 'seven';
someTing = 7

4.联合类型

联合类型表示可以取多种类型中的一中

什么是联合类型

let s1:number | string = 10;
s1 = 'seven';
s1 = 1;

联合类型使用 | 分隔每个类型。

这里的s1表示允许类型为numberstring ,而不能为其他类型

访问联合类型的属性或方法

function fn(s1:string|number):number{
    
    
    return s1.length
}
// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string|number'.
// Property 'length' does not exist on type 'number'.

访问的不是stringnumber的共同方法

访问 stringnumber 的共有属性是没问题的:

function fn(s1:string|number):number{
    
    
    return s1.toString()
}

联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型:

let s1:string | number;
s1 = '1111';
console.log(s1.length);
s1 = 7;
console.log(s1.length) // 报错

5.对象的类型-接口

使用对象定义类型

let name:{
    
    };
let name1:object;

一般实际中,我们不会这么定义类型,这样定义了,对象中的属性相当于any类型

let nName:{
    
    name:string,age:number};

指定对象中定义的类型,nameage属性名必须保存一致,不能多也不能少

let nName:{
    
    name:string,age?:number};

当前对象name传递的?.表示可传可不传;

let nName:{
    
    name:string,age?:number,[propName:string]:any}

[propName:string]表示传入一个未知的属性,必须是字符串类型的,[propName:string]:any的any表示子类型可以是任意值

let nName:{
    
    name:string,age?:number,[propName:string]:string}

指定子类型为string,如果传入了age属性就会报错,类型为string才行。

通常可以通过联合类型来解决这种问题

let nName:{
    
    name:string,age?:number,[propName:string]:string|number}

什么是接口

在TypeScript中,我们使用接口( Interfaces )来定义对象的类型。

在面对对象语言中,有个很重要的概念 ,它是对行为的抽象,而具体到如何行动需要由类(class)去实现( implement )

TypeScript的接口是一个非常灵活的概念,除了对类的一部分进行抽象以为,还可以对类的形状进行描述。

简单的例子

interface Person{
    
    
    name:string,
    age:number
}
let zhangshan:Person{
    
    
    name:'张三',
    age:16
}

定义了名叫Person的接口,在zhangsan中对象的形状必须要和Person一样,一般接口名称大写,用于区分。

interface Person {
    
    
    name: string;
    age: number;
}

let tom: Person = {
    
    
    name: 'Tom'
};
// index.ts(6,5): error TS2322: Type '{ name: string; }' is not assignable to type 'Person'.
//   Property 'age' is missing in type '{ name: string; }'.

定义的变量比接口少了一些属性是不允许的

interface Person {
    
    
    name: string;
    age: number;
}

let tom: Person = {
    
    
    name: 'Tom',
    age: 25,
    gender: 'male'
};

// index.ts(9,5): error TS2322: Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
//   Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.

多一些属性也是不允许的

可见,赋值的时候,变量的形状必须和接口的形状保持一致

可选属性

有时我们希望不要完全匹配一个形状,那么可以用可选属性:

interface Person{
    
    
    name:string,
    age?:number
}
let tom:Person={
    
    
    name:"丈夫"
}
// age可传可不传

这时, **仍不许添加新的未定义的属性 **

任意属性

有时候我们希望一个接口允许有任意的属性,可以使用如下方式

interface Person{
    
    
    name:string,
    age?:number,
    [proprsName:string]:any
}
let tom:Person={
    
    
    name:"丈夫",
    sss:1231231
}

使用 [propName: string] 定义了任意属性取 string 类型的值。

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

interface Person{
    
    
    name:string,
    age?:number,
    [proprsName:string]:string
}
let tom:Person={
    
    
    name:"丈夫",
    sss:"1231231"
}

指定任意类型为字符串,此时仍不能新增未定义的属性

interface Person{
    
    
    name:string,
    age?:number,
    [proprsName:string]:string
}
let tom:Person={
    
    
    name:"丈夫",
    age:10
    sss:"1231231"
}
// 报错,指定了sss子类型为string类型,age的类型为number

任意属性的值允许是 string,但是可选属性 age 的值却是 numbernumber 不是 string 的子属性,所以报错了 注意一个接口只能指定一个任意类型

一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型:

interface Person{
    
    
    name:string,
    age?:number,
    [proprsName:string]:string|number
}
let tom:Person={
    
    
    name:"丈夫",
    age:10
    gender:"男"
}

只读属性

interface Person{
    
    
    readonly id:number,
    name:string,
    age?:number,
    [proprsName:string]:string|number
}
let tom: Person = {
    
    
    id: 89757,
    name: 'Tom',
    gender: 'male'
};
tom.id = 9527;
// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.

上例中,使用 readonly 定义的属性 id 初始化后,又被赋值了,所以报错了

注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候

interface Person {
    
    
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    
    
    name: 'Tom',
    gender: 'male'
};

tom.id = 89757;
// 1. id未传 报错 2.id为只读,进行了第二次赋值 报错

6.数组的类型

在TypeScript中,定义数组的类型有多种方式,比较灵活

【类型+方括号】表示法

let fibonacci:number[] = [1,2,3,4,5];

数组中不允许出现其他的类型

let fibonacci:number[]= [1,'we',2,3]
//  Type 'string' is not assignable to type 'number'.

数组的一些方法的参数也会根据数组在定义时约定的类型进行限制

let fibonacci:number[]= [1,'we',2,3]
fibonacci.push('8')
// Argument of type '"8"' is not assignable to parameter of type 'number'.

要求传入number类型的,却传入string类型,报错

数组泛型

我们也可以使用数组泛型(Array Generic) Array 来表示数组

let fibonacci :<Array>number = [1,2,3,4,5];

用接口来表示数组类型

interface ArrayNumber{
    
    
    [propsName:number]:number
}

虽然接口也可以用来描述数组,但是我们一般不会这么做,因为这种方式比前两种方式复杂多了。

不过有一种情况例外,那就是它常用来表示类数组

类数组

类数组(Array -like Object)不是一个数组,比如arguments

function fn(){
    
    
    let args:number[] = arguments;
}
// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.

上例中,arguments 实际上是一个类数组,不能用普通的数组的方式来描述,描述的不全面,数组里不可能都是数字,而应该用接口的方式来定义

function fn(){
    
    
    let args:{
    
    
        [index:number]:number,
         length:number,
         callee:Function,
    } = arguments;
}

在这个例子中,我们除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有 lengthcallee 两个属性。

事实上常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection

functions sum(){
    let args:IArguments = arguments;
}

其中IArguments是TypeScript中定义好了的类型,它实际上就是:

interface IArguments{
    
    
    [index:number]:number,
    length:number;
    callee:Function;
}

any在数组中的使用

一个比较常见的做法是,用 any 表示数组中允许出现任意类型:

let list:<Array>any = ['xcatilu',25,{
    
    website:"http://localhost:3000"}]

7.函数的类型


函数是JavaScript的第一等公民


函数声明(匿名函数)

在JavaScript中,有两种常见的定义函数的方式–匿名函数(Function Declaration)和 函数表达式 ( Function Expression )

// 匿名函数
function sum(x,y){
    return x+y;
}
// 函数表示式
const sun = function(x,y){
    return x+y;
}

一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单:

function (x:number,y:number):number{
    
    
    return x+y
}

注意,输入多余的(或者少于要求的)参数,是不被允许的

function sum(x: number, y: number): number {
    
    
    return x + y;
}
sum(1, 2, 3);

// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.
function sum(x: number, y: number): number {
    
    
    return x + y;
}
sum(1);

// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.

函数表达式

如果要我们现在写一个对函数表达式(Function Expression)的定义,可能会写成这样:

let sum = function(a:number,b:number):number{
    
    
    return a+b
}

不过这样的实际上是对function进行的类型定义,而左边的sum则是推论出来的,正确的写法为

let sun:(x:number,y:number)=>number=function(x:number,y:number):number{
    
    
    return x+y;
}

注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>

在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

用接口定义函数的形状

我们也可以使用接口的方式来定义一个函数需要符合的形状

interface SearchFunc {
    
    
    (source:string,subString:string):boolean;
}
let mySearch:SearchFunc;
mySearch=function(source:string,subString:string){
    
    
    return source.search(subString)!==-1;
}

采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。

可选参数

前面提到,输入多余的或者少于要求的参数是不允许的。那么可选参数如何定义

functions fn(s1:string,s2?:string):string{
    
    
    return s1+' '+s2
}

需要注意的是,可选参数必须在必需参数后面,换句话说,可选参数后面不允许再出现必需参数了

function buildName(firstName?: string, lastName: string) {
    
    
    if (firstName) {
    
    
        return firstName + ' ' + lastName;
    } else {
    
    
        return lastName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName(undefined, 'Tom');

// index.ts(1,40): error TS1016: A required parameter cannot follow an optional parameter.

默认参数

在ES6中,我们允许给函数的参数添加默认参数,TypeScript会将添加的默认参数识别为可选参数。

functions fn(firstName:string,lastName:string='tom'){
    
    
    return firstName+' '+ lastName
}
fn('xiaoming','tools')
fn('yue')

此时就不受「可选参数必须接在必需参数后面」的限制了

functions fn(firstName:string='tom',lastName:string){
    
    
    return firstName+' '+ lastName
}
fn('xiaoming','tools')
fn('yue')

剩余参数

ES6中,可以使用…rest的方式获取函数中的剩余参数(rest参数)

function push(s1,...rest){
    
    
    rest.forEach(v=>s1.push(v));
}
let a:<Array>any = [];
push(a,1,2,3)

事实上,rest是一个数组。所以我们可以用数组的类型来定义它

function push(array:any[],...rest:any[]){
    
    
    rest.forEach(item=>array.push(item))
}
let a = [];
push(a, 1, 2, 3);

注意,rest参数只能是最后一个参数。

重载

重载允许一个函数接受不同数量或者类型的参数,做出不同的处理

functions reverse(x:string|number):string|number|void{
    
    
    if(typeof x === 'string'){
    
    
        return x.split('').reverse().join('')
    }else if(typeof x === 'number'){
    
    
        return Number(x.toString().split('').reverse().join(''))
    }
}

然而这样有一个缺点,就是不能够精确表达,输入为数字的时候,输出也应该是为数字,输入字符串的时候,输出也应该为字符串

这时,我们可以使用重载定义多个 reverse 的函数类型:

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
    
    
    if (typeof x === 'number') {
    
    
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
    
    
        return x.split('').reverse().join('');
    }
}

上例中,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。

注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

8.类型断言

类型断言(Type Assertion)可以用来手动指定一个值的类型。

as 类型

或者

<类型>

在 tsx 语法(React 的 jsx 语法的 ts 版)中必须使用前者,即 值 as 类型

形如 的语法在 tsx 中表示的是一个 ReactNode,在 ts 中除了表示类型断言之外,也可能是表示一个泛型

故建议大家在使用类型断言时,统一使用 值 as 类型 这样的语法。

let a:unknown;
a='xxxxx';
let b:string;
b= a as string;
interface Cat{
    
    
    name:string,
    run():void;
}
interface Fish{
    
    
    name:string,
    swim():viod;
}
function swim(animal:Cat|Fish){
    
    
    if(typeof (anumal as Fish).swim === 'function'){
    
    
        return true
    }
    return false
}
const cat:Cat={
    
    
    name:"张三"run(){
    
    
        console.log('run')
    }
}
swim(cat);

9.内置对象

JavaScript中有很多内置对象,它们可以之间在TypeScript中当作定义好了的类型

内置对象是根据标准在全局作用域上存在的对象,这里的标准指的是ECMAScript和其他环境(Dom)的标准

ECMAScript 的内置对象

ECMAScript 标准提供的内置对象有

BooleanErrorDateRegExp 等。

let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;

DOM 和 BOM 的内置对象

DocumentHTMLElementEventNodeList 等。

let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
  // Do something
});

它们的定义文件同样在 TypeScript 核心库的定义文件中。

库的定义文件中。

猜你喜欢

转载自blog.csdn.net/m0_51531365/article/details/123136164