typeScript入门

typeScriptJavaScript 的一个超集, typeScript 就是在JavaScript上做了一层封装,当然最终代码可以编译为 JavaScripttypeScript 增加了代码的可读性和可维护性,即使 typeScript 编译报错,也可以生成 JavaScript文件。

安装typeScript

全局安装

 npm install -g typescript

使用命令行tsc xxx.ts来编译ts文件

在JavaScript 中类型分为两种:原始数据类型和对象类型
原始数据类型包括:布尔值、数值、字符串、nullundefined 以及 ES6 中的 Symbol

布尔值

布尔值是最基本的数据类型,在ts中使用boolean 来定义布尔类型

let isDone: boolean = false;

let isDone: boolean = new Boolean(1); //注意,使用new Boolean()创造的对象不是布尔值
//Type 'Boolean' is not assignable to type 'boolean'. 'boolean' is a primitive, 
//but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.

let isDone: boolean = Boolean(1);//直接调用Boolean,返回的是一个布尔类型

数字类型

使用 number去定义 除了支持十进制和十六进制,TypeScript还支持二进制和八进制。

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

编译结果:

var decLiteral = 6;
var hexLiteral = 0xf00d;
var binaryLiteral = 10;
var octalLiteral = 484;
var notANumber = NaN;
var infinityNumber = Infinity;

二进制和八进制编译时会转化成十进制

字符串

使用string来定义

let name: string = "小明"

// 模板字符串
let hello = `Hello, my name is ${myName}`

Null 和 Undefined

使用 nullundefined 来定义这两个原始数据类型

let u: undefined = undefined;
let n: null = null;

默认情况下nullundefined是所有类型的子类型

// 这样不会报错
let num: number = undefined;

任意值

我们想要为那些在编程阶段还不清楚类型的变量指定一个类型,比如用户输入的,或者第三库的代码,可以使用any类型来标记这些变量

如果是普通类型,在赋值过程中改变类型是不允许的

let n: number = 1
n = "小明" //Type '"小明"' is not assignable to type 'number

如果是any类型,在赋值过程中改变类型是允许的

let n: any= 1
n = "小明"

在任意值上访问任何属性调用任何方法都是允许的

let n: any= 1
console.log(n.name)
console.log(n.getData())

变量在声明过程中,未指定类型,那么他会识别成任意类型

let n;
n = 1
n = "小明"

void

void类型像是与any类型相反,它表示没有任何类型,当一个函数没有返回值时,你会看到他的返回类型是void

function getData(): void {
  console.log("getData")
}

声明一个void类型的变量没什么意义,他只能赋值undefined,null

let v: void = undefined;
v = null

undefinednull类型相似,他们之间的区别是undefinednull类型相是所有类型的子类型

let n:number = 1
let u:void = null ;
// 这样不会报错
n = null 
n = undefined
//这样会报错
n = v //Type 'void' is not assignable to type 'number'.

联合类型

值可以为多种类型中的一种,使用|来分隔每个类型

let a: number | string ;
// 表示 允许 a的类型是number 或者 string,但是不能是其他类型
a = "小明"
a = 18
a = true //报错

ts不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法

function getLength(str: number | string ){
  console.log(str.length)
}
//报错

因为length 不是 stringnumbe 的共有属性,所以会报错
访问stringnumbe 的共有属性是没问题的

function getLength(str: number | string ){
  str.toString()
}

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

let a:number |string;
a = "小明"
console.log(a.length)
a = 13
console.log(a.length) //报错 Property 'length' does not exist on type 'number'.

类型推论

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

let a = 7// => let a: number= 7
a = "小明" // 报错

如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成any类型,而不被类型检查

let a; // => let a: any;
a = 7
a = "小明" 

接口

ts中,我们用接口(Interfaces)来定义对象类型

interface Person {
    age:number,
    name: string
}
let xiaoming: Person = {
    age:18,
    name:"小明"
}

上述代码我们定义了一个接口Person,然后定义了一个类型是Person的变量xiaoming
这样,我们就约束了变量xiaoming的形状要与接口Perso一致。接口的首字母尽量大写,
定义的变量比接口少一些属性或者多一些属性都是不允许的。

有时候我们需要某些属性可有可无的,这时候我们可以用到可选属性,用?来表示,

interface Person {
   name: string,
   age?:number,
}
let xiaoming: Person = {
    name:"小明"
}
let zhangsan: Person = {
    name:"张三",
   age: 18
}

可选属性,表是该属性可以不存在,这时候还是不能添加未定义的属性
如果我们想要添加未定义的属性,我们使用 任意属性 来实现

interface Person {
    name: string,
    age?:number,
    [name:string]:any
}
let xiaoming: Person = {
    age:18,
    name:"小明",
    height: 170,
}

[name:string]:any表示定义了一个值为any的任意属性

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

interface Person {
    name: string,
    age?:number,
    [name:string]:string
}
let xiaoming: Person = {
    age:18,
    name:"小明",
    height: '170cm',
}
//报错
//类型“number”的属性“age”不能赋给字符串索引类型“string”

因为任意属性的值允许类型是string,而age的值类型是number,number类型不是任意属性值允许类型的子类型
一个接口中只能定义一个任意属性,可选属性可以是多个。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型

interface Person {
    name: string,
    age?:number,
    [name:string]:string | number
}
let xiaoming: Person = {
    age:18,
    name:"小明",
    height: '170cm',
}

有时候我们希望对象中的一些字段只能在创建的时候被赋值,我们可以用readonly来实现

interface Person {
    readonly  name: string,
    age?:number,
    [name:string]:string | number
}
let xiaoming: Person = {
    name:"小明",
    age:18,
    height: '170cm',
}
xiaoming.name = "张三" //报错 无法分配到 "name" ,因为它是只读属性

只读是约束在第一次给对象赋值,而不是第一次给只读属性赋值

数组

ts中,数组的定义方式有多种,

  • 最简单的是类型[]
let arr: number[] = [1,2,3,4]

不允许其他类型出现,undefinednull除外(undefinednull是所有类型的子类型)

let arr: number[] = [1,2,3,4,"5"] //报错 不能将类型“string”分配给类型“number”
  • 也可以用数组泛型Array<类型>来表示数组
let arr: Array<number> = [1,2,3,4]
  • 当然也可以用接口来描述数组,一般不用这种方式,因为没有前两种简单,常用他来表示类数组,类数组不是数组类型,比如arguments
function todo(){
    let arg:Array<number> = arguments
}
// 已声明“arg”,但从未读取其值。ts(6133)
//类型“IArguments”缺少类型“number[]”的以下属性: pop, push, concat, join 及其他 24 项。ts(2740)

使用接口描述数组

interface ITodo {
    [index: number]: any;
    length: number;
    callee: Function;
}
function todo(){
    let arg:ITodo  = arguments
}

函数

函数的表达式常见的有两种:函数声明,函数表达式

//函数声明
function todo(){
  ....
}
//函数表达式
let todo = function(){
 ...
}

函数申明和函数表达式的主要区别:函数声明整体会被提升到当前作用域的顶部,函数表达式也提升到顶部但是只有其变量名提升

函数是有输入和输出的,在ts中要对其进行约束,函数声明的类型定义如下

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

上述例子是,参数anumber类型,参数bnumber类型,函数返回值是number类型,函数多传入或者少传入参数都是不允许的

函数表达式的声明如下

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

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

有些参数是可选的,前面提了多传入或者少传入参数都是不允许的,这时我们可以用可选参数?来解决

function sum(a:string,b?:string):string{
    return b?a+b:a
}
sum('xiao')

注意,可选参数后面不能有必要参数

有时候某些参数需要默认值,


function sum(a: number, b: number=1): number{
  return a+b
}
sum(1)
//没有可选参数的那种限制
function sum1(a: number=1, b: number): number{
  return a+b
}
sum1(undefined,1)

类型断言

类型断言就是用来指定值的类型,比如将一个联合类型指定成其中一个类型
语法

值 as 类型
或者
<类型>值

TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型中共有的属性或方法

interface Tom {
    name: string;
    run(): void
}
interface Jack {
    name: string;
    todo():void
} 

function getName(people:Tom|Jack){
    console.log(people.name)
}

有时候,我们需要在还不确定类型的时候就访问其中一个类型特有的属性或方法,比如

interface Tom {
    name: string;
    run(): void
}
interface Jack {
    name: string;
    todo():void
} 

function getName(people:Tom|Jack){
    if(typeof people.run === "function"){
       ....
    }
}
//这时候会报错
//类型“Tom | Jack”上不存在属性“run”。类型“Jack”上不存在属性“run”

这时候我要就要用类型判断,将people类型指定为Tom

interface Tom {
    name: string;
    run(): void
}
interface Jack {
    name: string;
    todo():void
} 

function getName(people:Tom|Jack){
    if(typeof (people as Tom).run === "function"){
       ....
    }
}

类型判断只能ts编译器,无法避免运行错误,不能滥用

interface Tom {
    name: string;
    run(): void
}

interface Jack {
    name: string;
    todo():void
} 
let tom:Tom = {
    name:"tom",
    run(){console.log('我是tom')}
}
function getName(people:Tom|Jack){
    (people as Jack).todo()
}
getName(tom)
//报错 people.todo不是个函数

getName函数接收的参数类型是Tom|Jack,people as Jack隐藏了peopleTom的情况,直接将people的类型断言成Jack,ts编辑相信了这个断言,故在调用 todo()时没有编译错误。

将任何类型断言成any
当我们引用一个在此类型上不存在的属性或方法时,就会报错

let n: number = 1
n.length
//类型“number”上不存在属性“length”

这样的报错对我们来说很有用,

有时候,我们知道之这段代码运行绝对没有问题

window.i = 1//类型“Window & typeof globalThis”上不存在属性“i”

上诉代码ts编辑器提示window上不存在这个属性,我们可以将window断言成any来解决

(window as any).i = 1

在 any 类型的变量上,访问任何属性都是允许的

泛型

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

举个例子

function getArray(length:number,value:any):Array<any> {
    let arr:Array<any> = []
    for(let i = 0 ;i < length ;i++){
        arr.push(value)
    }
    return arr
}
getArray(3,"3") //["3","3","3"]

这段代码,编译没有问题,但是他没有定义一个准确的返回类型,Array<any>允许数组的每一项为任意类型,我们的预期是数组的每一项都是我们输入的value的类型,这时候我们就可以用泛型l来实现我们想要的

function getArray<T>(length:number,value:T):Array<T> {
    let arr:Array<T> = []
    for(let i = 0 ;i < length ;i++){
        arr.push(value)
    }
    return arr
}
getArray<string>(3,"3") //["3","3","3"]

我们在函数名后添加了<T>,其中 T 用来指输入的类型可以为任意类型,在后面的输入 value: T 和输出Array<T> 中即可使用了,getArray<string>这个指定我们输入的类型,当然我们也可以不用指定,让类型推断来推断输入类型

function getArray<T>(length:number,value:T):Array<T> {
    let arr:Array<T> = []
    for(let i = 0 ;i < length ;i++){
        arr.push(value)
    }
    return arr
}
getArray(3,"3") //["3","3","3"]

有时候我们的函数有多个类型,定义泛型的时候也可以定义多个参数类型

function people<T,U>(name:T,age:U): [T, U]{
    return [name,age]
}
people("张三", 18)

在使用泛型的时候,因为不知道他是什么类型,所以不能随意操作他的方法或者属性

function getLength<T>(str:T): T{
    console.log(str.length)
    return str
}
getLength("xiaoming")
//报错  类型“T”上不存在属性“length”

这时候我们就要对泛型进行约束,这个函数只允许接收含有length的属性的变量

interface Ilength {
    length:number
}
function getLength<T extends Ilength >(str:T): T{
    console.log(str.length)
    return str
}
getLength("xiaoming")

我们使用了extends来对T进行约束,只允许输入和接口Ilength结构相同的数据,如果输入变量不包含length属性,ts编译阶段就会报错

interface Ilength {
    length:number
}
function getLength<T extends Ilength >(str:T): T{
    console.log(str.length)
    return str
}
getLength(112) //报错 类型“number”的参数不能赋给类型“Ilength”的参数

当然多个参数类型之间也可以相互约束

function peoples<T extends U,U>(obj:T,obj1:U): T{
    return obj
}
peoples({name:"张三",age: 18},{name:"李四"})

泛型参数可以有默认值

function getArray<T =  string>(length:number,value:T): Array<T> {
    let arr:Array<T> = []
    for(let i = 0 ;i < length ;i++){
        arr.push(value)
    }
    return arr
}

泛型接口

之前我们可以用接口来定义函数

interface IlengthFn {
    (str:string):number 
}

let hetLength:IlengthFn
hetLength = function(str:string):number{
    return str.length
}

当然也可以用泛型接口来定义函数

interface IlengthFn <T>{
    (str: T):number  
}
interface IIlength{
    length:number
}
let hetLength:IlengthFn<any>
hetLength = function<T extends IIlength>(str:T):number{
    return str.length
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342