TS 入门

原文地址:TS入门学习_卖猹的西瓜君的博客-CSDN博客_ts学习

一、TS是什么?
官方介绍:TypeScript 是 JavaScript 的超集,这意味着它可以完成 JavaScript 所做的所有事情,而且额外附带了一些能力。
Typed JavaScript at Any Scale. 它强调了 TypeScript 的两个最重要的特性——类型系统、适用于任何规模。
我们知道,JavaScript 是一门非常灵活的编程语言,

它没有类型约束,一个变量可能初始化时是字符串,过一会儿又被赋值为数字。
由于隐式类型转换的存在,有的变量的类型很难在运行前就确定。
基于原型的面向对象编程,使得原型上的属性或方法可以在运行时被修改。
函数是 JavaScript中的一等公民,可以赋值给变量,也可以当作参数或返回值。
JavaScript 本身是一种动态类型语言,这意味着变量可以改变类型。从 TypeScript 的名字就可以看出来,「类型」是其最核心的特性。使用 TypeScript 的主要原因是就是为了给 JavaScript 添加静态类型。静态类型意味着变量的类型在程序中的任何时候都不能改变。它可以防止很多bug !

Ts是静态类型
类型系统按照「类型检查的时机」来分类,可以分为动态类型和静态类型。

动态类型是指在运行时才会进行类型检查,这种语言的类型错误往往会导致运行时错误。JavaScript 是一门解释型语言,没有编译阶段,所以它是动态类型
静态类型是指编译阶段就能确定每个变量的类型,这种语言的类型错误往往会导致语法错误。TypeScript 在运行前需要先编译为 JavaScript,而在编译阶段就会进行类型检查,所以 TypeScript 是静态类型

TypeScript 是弱类型
类型系统按照「是否允许隐式类型转换」来分类,可以分为强类型和弱类型。

TypeScript 是完全兼容 JavaScript 的,它不会修改 JavaScript 运行时的特性,所以它们都是弱类型。
python是强类型,在处理以下代码时需要强制转换类型后才不会报错

print(str(1) + '1')  // 打印出字符串 '11'
1
强/弱是相对的,Python 在处理整型和浮点型相加时,会将整型隐式转换为浮点型,但是这并不影响 Python 是强类型的结论,因为大部分情况下 Python 并不会进行隐式类型转换。相比而言,JavaScript 和 TypeScript 中不管加号两侧是什么类型,都可以通过隐式类型转换计算出一个结果——而不是报错——所以 JavaScript 和 TypeScript 都是弱类型。

虽然 TypeScript 不限制加号两侧的类型,但是我们可以借助 TypeScript 提供的类型系统,以及 ESLint 提供的代码检查功能,来限制加号两侧必须同为数字或同为字符串。这在一定程度上使得 TypeScript 向「强类型」更近一步了——当然,这种限制是可选的。

这样的类型系统体现了 TypeScript 的核心设计理念[6]:在完整保留 JavaScript 运行时行为的基础上,通过引入静态类型系统来提高代码的可维护性,减少可能出现的 bug。

二、安装TS
1.引入库
TypeScript 的命令行工具安装方法如下:

npm install -g typescript

以上命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了。

编译 TypeScript 文件:

tsc hello.ts

我们约定使用 TypeScript 编写的文件以 .ts 为后缀,用 TypeScript 编写 React 时,以 .tsx 为后缀。
但是每次修改ts文件都要运行一遍tsc很麻烦,使用了vscode编译器配置

在当前文件夹下执行tsc --init 生成文件 tsconfig.json
terminal中现在run task 

三、TS基础
原始类型
javascript有七种原始数据类型,布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol 和 ES10 中的新类型 BigInt

let isDone: boolean = false; //布尔值

let decLiteral: number = 6; //数值,es6中的其他进制会被编译为十进制
let notANumber: number = NaN;
let infinityNumber: number = Infinity;

let myName: string = 'Tom'; //字符串
// 空值 用来赋值给没有返回数据的函数
function alertName():void{
  alert('my name is haha')
}
//声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null
let unusable: void = undefined;

let name:undefined = undefined //
let n: null = null;
//与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量,而 void 类型的变量不能赋值给 number 类型的变量
let a:undefined
let a:number = 1



数组
let ids: number[] = [1, 2, 3, 4, 5]; // 只能包含 number
let names: string[] = ['ConardLi', 'Tom', 'Jerry']; // 只能包含 string
// let options: boolean[] = [true, false, false]; 只能包含 true false
let books: object[] = [
  { name: 'Tom', animal: 'cat' },
  { name: 'Jerry', animal: 'mouse' },
]; // 只能包含对象
let arr: any[] = ['hello', 1, true]; // 啥都行,回到了 JS

ids.push(6);
// ids.push('7'); // ERROR: Argument of type 'string' is not assignable to parameter of type 'number'.

// 数组可以包含多个类型
let person: (string | number | boolean)[] = ['ConardLi', 1, true];
person[0] = 100;
// person[1] = {name: 'ConardLi'} // Error - person array can't contain objects

// 如果数组有默认值,ts也会进行类型推断

//ts中可以定义一种特殊类型的数组,元组,元组是具有固定大小和已知数据类型的数组,他比常规数组更严格
let persona:[string,number,boolean]=['conaeadli',1,true]
// persona[0]=17  //Error - Value at index 0 can only be a string


对象
在定义对象的类型时,我们通常会使用 interface。如果我们需要检查多个对象是否具有相同的特定属性和值类型时,是很有用的:

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

let person1: Person = {
  name: 'ConardLi',
  age: 17,
  isProgrammer: true,
};

let person2: Person = {
  name: 'Tom',
  age: 3,
  isProgrammer: false,
};

我们还可以用函数的类型签名声明一个函数属性,通用函数(sayHi)和箭头函数(sayBye)都可以声明:

interface Animal{
  eat(name:string):string;
  speaak:(name:string)=>string;
}
let tom:Animal={
  eat:function(name:string){
    return `eat${name}`
  },```speaak:(name:string)=>`speak ${name}`
}
// let a=tom.eat('zeze')
// let b=tom.speaak('hh')
console.log(tom.eat('zeze'),tom.speaak('hh'))

函数
我们没必要明确声明 circle 是一个函数,TypeScript 会进行类型推断。TypeScript 还会推断函数的返回类型,但是如果函数体比较复杂,还是建议清晰的显式声明返回类型。

const circle = (diam: number): string => {
  return '圆的周长为:' + Math.PI * diam;
};
//我们可以在参数后添加一个?,表示它为可选参数;另外参数的类型也可以是一个联合类型:
const add = (a: number, b: number, c?: number | string) => {
  console.log(c);
  return a + b;
};

console.log(add(5, 4, '可以是 number、string,也可以为空'));

any基本上可以将 TypeScript 恢复为 JavaScript

Dom和类型转换
ts没办法像js那样访问dom,这就意味着ts没办法确定这些dom是否存在,所以使用非空断言,我们可以明确的告诉编译器这个表达式是否是null/undefined, ts可以通过类型推断确认它的类型为 HTMLAnchorElement。

非空断言 x! 将从 x 值域中排除 null 和 undefined.

const link = document.querySelector('a')!
console.log(link)  //null

//如果我们需要通过 class 或 id 来选择一个 DOM 元素时,通过类型转化来实告知ts,该元素存在且知道他的类型时

HTMLFormElement

const from = document.getElementById('form') as HTMLAnchorElement
console.log(from)


可以定义类中的每条数据的类型

class Person{
  name:string;
  iscool:boolean;
  age:number;
  constructor(n:string,c:boolean,a:number){
    this.name=n
    this.iscool=c
    this.age=a
  }
  sayHi(){
    return `Hi,woshi ${this.name},jinnian ${this.age}`
  }
}

const person3=new Person('haha',true,18)
console.log(person3.sayHi())

创建一个仅包含person类的数组对象

let people:Person[]=[person3]
console.log(people)

类的修饰符readonly,private,protected,public

class Pobj{
  readonly name:string;   //不可以更改
  private iscool:boolean;   //pobj类的私有属性,外部访问不到
  protected email:string;  //只能从这个类和他的子类中进行访问和修改
  public age:number;   
  constructor(n:string,c:boolean,e:string,a:number){
      this.iscool=c
      this.name = n
      this.age = a
      this.email=e
  }
  sayHi(){
    return `Hi,woshi ${this.name},jinnian ${this.age}`
  }
}
const p1 = new Pobj('ConardLi', true, 'conard@xx.com', 17);
console.log(p1); // ConardLi
// p1.name = 'Jerry';


我们可以通过下面的写法,属性会在构造函数中自动分配,我们类会更加简洁:

class Pobje{
  constructor(
    readonly name:string,  //不可以更改
    private iscool:boolean,  //pobj类的私有属性,外部访问不到
    protected email:string, //只能从这个类和他的子类中进行访问和修改
    public age:number,  
  ){}
  sayHi(){
    return `Hi,woshi ${this.name},jinnian ${this.age}`
  }
}

接口
接口(Interfaces)可以用于对「对象的形状(Shape)」进行描述和对类的一部分行为进行抽象。

//定义了对象的外观
interface Person {
  name: string;
  age: number;
}

function sayHi(person: Person) {
  console.log(`Hi ${person.name}`);
}

sayHi({
  name: 'ConardLi',
  age: 17,
}); // Hi ConardLi


//对类的一部分行为进行抽象
//门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现

interface alarm{
  alert():void;
}
class Door{
}
class SecurityDoor extends Door implements alarm{
  lightOn() {
    throw new Error("Method not implemented.")
  }
  lightOff(): void {
    throw new Error("Method not implemented.")
  }
  alert(){
    console.log('securitydoor')
  }
}

class Car implements alarm{
  lightOn() {
    throw new Error("Method not implemented.")
  }
  lightOff(): void {
    throw new Error("Method not implemented.")
  }
  alert(){
    console.log('car')
  }
}


你还可以使用类型别名定义对象类型:


type Person = {
  name: string;
  age: number;
};

或者可以直接匿名定义对象类型:


function sayHi(person: { name: string; age: number }) {
  console.log(`Hi ${person.name}`);
}

interface 和 type 非常相似,很多情况下它俩可以随便用,比如它们两个都可以扩展:

扩展 interface:

interface Animal {
  name: string
}

interface Bear extends Animal {
  honey: boolean
}

const bear: Bear = {
  name: "Winnie",
  honey: true,
}

扩展 type:

type Animal = {
  name: string
}

type Bear = Animal & {
  honey: boolean
}

const bear: Bear = {
  name: "Winnie",
  honey: true,
}

interface 是可以自动合并类型的,但是 type 不支持。

类的interface
泛型
泛型可以让我们创建一个可以在多种类型上工作的组件,它能够支持当前的数据类型,同时也能支持未来的数据类型,这大大提升了组件的可重用性。

const addID = (obj:Object)=>{
  let id = Math.floor(Math.random()*1000)
  return {...obj,id}
}
let Id1=addID({name:'ha',age:10})
console.log(Id1)
console.log(Id1.name) //编译器中标红,但浏览器可以打出ha  //Property 'name' does not exist on type '{ id: number; constructor: Function; toString(): 

那么,我们怎么将任意对象传递给 addID,而且仍然可以告诉 TypeScript 该对象具有哪些属性和值?这种场景就可以使用泛型了, – T 被称为类型参数:在将一个对象传入函数时,已经告诉了ts来捕获他的类型le

const addIds = <T>(obj:T)=>{
  let id = Math.floor(Math.random()*1000)
  return {...obj,id}
}

let Id2=addIds({name:'ha',age:10})
console.log(Id2.name)

这样会出现问题:什么都可以传入函数,ts捕获类型后并不会报告问题,所以我们需要一个约束,通过将泛型类型T作为object的扩展,来告诉ts直接收对象

const addIDs=<T extends object>(obj:T)=>{
  let id = Math.floor(Math.random()*1000)

  return {...obj,id}
}

let q = addIDs({name:'zze',age:'30'})
console.log(q.name)
let w = addIDs('sds') //Argument of type 'string' is not assignable to parameter of type 'object'.
// console.log(w)

但是在js中数组也是对象,传入数组还是会逃避类型检查,所以使用object的参数应该带一个有值是字符串的name属性

const addIDString = <T extends {name:string}>(obj:T)=>{
  let id = Math.floor(Math.random()*1000)
  return {...obj,id}
}
let s = addIDString({name:'jj',age:6})
console.log(s.age)
// let sa = addIDString(['kk','sd'])//Property 'name' is missing in type 'string[]' but required in type '{ name: string; }'

所以泛型允许在参数和返回类型提前未知的组件中具有类型安全

如果需要接受多个类型的函数最好使用泛型而不是any

使用一个泛型来扩展接口,确保传入的参数都有一个length属性

interface haslength{
  length:number
}
const showLength = <T extends haslength>(obj:T)=>{
  console.log(obj.length,55)
  return obj.length
}
showLength('hello')  //5
// showLength(8) //Argument of type 'number' is not assignable to parameter of type 'haslength'.

泛型接口
当不知道对象中的某个值是什么类型时就用泛型来传递类型

interface Persien<T>{
  name:string,
  age:number,
  document:T
}
const Persien1:Persien<string[]>={
  name:'conal',
  age:1,
  document:['passport','bank']
}
const Persien2:Persien<string>={
  name:'da',
  age:2,
  document:'passport,banl'
}

枚举
枚举允许我们定义或声明一组相关值,可以是数字或者字符串作为一组命名常量
默认枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射,第一位赋值后,其他值依次递增

enum week{
  Sun, Mon, Tue, Wed, Thu, Fri, Sat
}
console.log(week.Sun)  //0
console.log(week.Mon)  //1

类型收窄
变量可以从不太精确的类型转移到更精确的类型,这个过程称为类型收窄。


interface vehicle{
  topSpeed:number
}

interface Train extends vehicle{
  carriages:number
}
interface Plane extends vehicle{
  wingspan:number
}

type planeOrTrain = Train|Plane
function getspeedRadio(v:planeOrTrain){
  // console.log(v.carriages)//ERROR: 'carriages' doesn't exist on type 'Plane'
}


由于 getSpeedRatio 函数处理了多种类型,我们需要一种方法来区分 v 是 Plane 还是 Train 。我们可以通过给这两种类型一个共同的区别属性来做到这一点,它带有一个字符串值:

interface Train extends vehicle{
  carriages:number
  type:'Train'
}
interface Plane extends vehicle{
  wingspan:number
  type:'Plane'
}

type planeOrTrainnew = Train | Plane

function getnewspeedRadio(v:planeOrTrainnew){
  if(v.type==='Train'){
    return v.topSpeed/v.carriages
  }
  return v.topSpeed/v.wingspan
}

let bigTrain:Train={
  type:'Train',
  topSpeed:100,
  carriages:20
}

let speed = getnewspeedRadio(bigTrain)
console.log(speed)

四、React 和Typescript
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值