TypeScript是JavaScript的超集,为JS增加了类型支持。
目的:在编译的时候检索错误,提前发现错误减少找bug改bug的时间。代码提示更多。维护性更强。支持最新的ECMAScript语法。
安装:npm i -g typescript Node.js和浏览器只认JS代码,要将TS转为JS才能执行 tsc hello.ts(将TS编译为JS)
简化TS运行步骤:安装 npm i -g ts-node ts-node hello.ts(不会生成js文件)
TS会检查变量类型的变化,而JS不会
类型注解: :number(e为变量添加类型约束)
JS类型:原始类型(number/string/boolean/null/undefined/symbol)=》Symbol是ES6中引入的一种新的基本数据类型,用于表示一个独一无二的值。
对象类型 object(包括数组、对象、函数等对象)
TS新增类型:联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any等
let s:symbol = Symbol()
let numbers:number[] = [1,3,5] (推荐写法,效果和下一行相同)
let strings:Array<string> = ['1','2','3']
let arr:(number | string)[] = [1,2,'a'](联合类型,数组内支持number和string)
类型别名:type CustomArray = (number | string | null)[]
let arr1:CustomArray = [1,2,'a',null]
函数:const add = (num1:number,num2:number):number => {
return num1 + num2
}
等价于=》const add:(num1:number,num2:number) => number = (num1,num2) => {
return num1 + num2
}
若函数没有返回值,需要指定返回值类型为void
function mySlice(start?:number,end?:number) { //属性后加?:用以判断该属性是否可选,可选参数后不能出现必选参数
console.log("起始索引",start,"结束索引",end)
}
对象类型:
let person: {name:string,age:number,sayHi():void,greet(name:string):void} = {
name:'zs',
age:18,
sayHi() {},
greet(name) {},
}
let person: {
name:string
age:number
sayHi:() => void
greet(name:string):void
} = {
name:'zs',
age:18,
sayHi() {},
greet(name) {},
}
接口:interface IPerson {
name:string
age:number
sayHi():void
}(相比type类型别名,接口只能为对象指定类型)
接口继承 :(P2继承P1属性)interface P1 {x:number,y:number} interface P2 extends P1 {z:number}
元组:let position:[number,string] = [1,'333'](约束数组内元素数量、类型)
声明变量并初始化、决定函数返回值时,发生了类型推论,可以省略类型注解。
类型断言: 使用as关键字实现类型断言
const alink = document.getElementById('link') as HTMLAnchorElement
const alink = <HTMLAnchorElement>document.getElementById('link')
字面量类型:(const定义的常量,它的值不能变换)多用于数据明确不变更的情况
let str1 = "Hello TS" 类型=》srting
const str2 = "Hello TS" 类型=》"Hello TS"
字面量类型配合联合类型一起使用:const changDirection = (direction:'up'|'down'|'left'|'right')=>{}
等价于枚举(一般推荐使用字面量类型+联合类型组合的方式,更加直观,但枚举提供值 )
枚举:(枚举的值默认从0开始,指定值后按指定值累加)
enum Direction {up, down,left,right}
const changDirection = (direction:Direction)=>{ }
changDirection(Direction.up)//访问直接通过 (.)语法访问枚举成员
枚举成员有默认值(值为数字的称为数字枚举),从0开始自增,也可以设置初始化值
enum Direction {up=2, down=4,left=6,right=8}
字符串枚举:枚举成员的值是字符串。字符串枚举后面的每个成员必选有初始值
enum Direction {up, down='1',left=4,right}
编译为JS代码时,其他类型会被自动移除,枚举类型会被编译成JS代码
any类型:不推荐使用any,会失去类型保护,可以临时使用
隐式具有any的情况(1.声明变量不提供类似和默认值。2.函数参数不加类型)
typeof:TS的typeof可以在类型上下文中引用变量或属性的类型
let p = {x:1,y:2}
function formatpoint(point:typeof p) {}
formatpoint({x:3,y:4})
TypeScript高级类型
class类:TS中的class,不仅提供了class的语法功能,也作为一种类型存在。
class Person {
age:number
gender:string
constructor(age:number,gender:string) {//构造函数不能指定返回值类型,用以指定初始值
this.age = age
this.gender = gender
}
}
const p = new Person(22,'man')
类的继承:类继承的两种方式implements(实现接口)、extends(继承父类)
JS中只有extends,implements是TS提供
class A {} class B extends A {} 或者 interface A {} class B implements A{}//B类必须提供A接口中指定的所有方法和属性
类成员可见性:可以使用TS来控制class的方法或属性对于class外的代码是否可见。
public(公有的,默认)、protected(受保护的,只能在当前类和子类中调用)、private(私有的,只能在当前类调用,也无法通过实例来访问)
readonly(只读修饰符):防止在构造函数之外对属性进行赋值,只能修饰属性而不能修饰方法
class Person {
readonly age:number = 2//不添加类型约束,age会变成字面量类型
constructor(age:number) {
this.age = age
}
}
类型兼容性:TS采用结构化类型系统,类型检查只要值所具有的形状相同就属于同一类型。成员不同时,成员多的可以赋值给少的(class和接口的兼容性差不多,接口兼容报错,需要赋初始值)
class A { x:number,y:number}
class B {x:number,y:number,z:string}
const a:A = new B()
函数之间兼容性:参数少的可以赋值给参数多的、相同位置的参数类型要相同、只关注返回值类型本身
交叉类型(&):类似于接口继承,用于组合多个类型为一个类型(常用于对象类型)
interface Person {name:string}
interface Contact {phone:number}
type PersonDetail = Person & Contact
交叉类型可以处理含同名属性的类型冲突( | ),接口继承报错
泛型<>:相较于any,泛型在保证类型安全前提下,让函数等于多种类型一起工作,从而实现复用(常用于函数、接口、class)
function id<Type>(value:Type):Type {return value}
const num = id<number>(10)
const str = id<string>("hello")
简化调用:const num = id(10)、const num = id("hello")类型推断为字面量id和hello
泛型约束:不添加类型约束导致无法访问任何属性(如数组的length)
function id<Type>(value:Type[]):Type[] { value.length return value}(指定更加具体的类型)
interface ILength {length:number}//添加约束
function id<Type extends ILength>(value:Type):Type {
value.length
return value
}
id(['1',2,3]) id('123123') id({length:12,name:'zs'})(传入的实参只要有length属性即可)
添加多个类型变量,并且变量之间还可以约束
function getProp<Type,Key extends keyof Type>(obj:Type,key:Key) {
return obj[key]
}
getProp({name:'jack',age:22},'age') getProp({name:'jack',age:22},'name') getProp('abc',1)//1表示索引
keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
泛型接口:需要显示指定具体的类型没有类型推断
interface IdFunc<Type> {
id:(value:Type) => Type
ids:() => Type[]
}
let obj:IdFunc<number> = {
id(value) {
return value
},
ids() {
return [1,2,3]
}
}
泛型类
class Person<NumType> {
defaultValue:NumType
add:(x:NumType,y:NumType) => NumType
constructor(value:NumType) {
this.defaultValue = value
}
}
const myNum = new Person<number>(10)
const myNum = new Person(10)//加了constructor可以省略<类型>不写
myNum.defaultValue = 10
泛型工具类型:
1.Partial<Type>用来构造(创建)一个类型,将Type的所有属性设置为可选。
interface Props {
id:string
children:number[]
}
type PartialProps = Partial<Props>
let p1:Props = {
id:'123',
children:[1,2,3]
}
let p2:PartialProps = {//可以不传值,属性都是可选的
id:'1'
}
2.Readonly<Type>用来构造一个类型,将Type的所有属性都设置为Readonly(只读)
type ReadonlyProps = Readonly<Props>
3.Pick<Type,Keys>从Type中选择一组属性来构造新类型。第二个类型变量传入的属性只能是第一个类型变量中存在的属性
interface Props {
id:string
title:string
children:number[]
}
type PickProps = Pick<Props,'id'|'title'>
4.Record<Keys,Type>构造一个对象类型,属性键为Keys,属性类型为Type。前者表示对象有哪些类型,后者表示对象属性的类型
type RecordObj = Record<'a'|'b'|'c',string[]>
type RecordObj = {//等价于上式
a:string[]
b:string[]
c:string[]
}
let obj:RecordObj = {
a:['a'],
b:['b'],
c:['c']
}
索引签名类型:(无法确定对象中有哪些属性,或对象中可能出现任意多个属性
interface AnyObject {
[key:string]:number//前置知识:JS中对象({})的键是string类型的。key:string可以代表对象所有属性的键
}
let obj:AnyObject = {
a:1,
b:12,
c:123
}
interface MyArray<T> {
[index:number]:T//数组的索引(键)是number类型
}
let arr1:MyArray<number> = [1,2,3]
映射类型:基于旧类型创建新类型(对象类型),减少重复、提示开发效率。 注意:映射类型只能在类型别名中使用,不能在接口中使用。
//根据联合类型创建
type PropKeys = 'x'|'y'|'z'
type Type1 = {x:number,y:number,z:number}
type Type2 = {[Key in PropKeys]:number}//映射
//根据对象类型创建。keyof Props获取到对象类型Props中所有键的联合类型,key in Props表示可以是Props中所有的键名中的任意一个
type Props = {a:number,b:string,c:null}
type Type3 = {[key in keyof Props]:number}
T[p]语法(索引查询类型):查询属性的类型,T是对象,p是对象的属性
type Props = {a:number,b:string,c:null}
type TypeA = Props['a']
type MyPartial<T> = {//模拟Partial类型
[P in keyof T]?:T[P]
}
type PartialProps = MyPartial<Props>
//同时查询多个索引的类型
type Props = {a:number,b:string,c:null}
type TypeA = Props['a'|'b']
type TypeB = Props[keyof Props]
类型声明文件: .d.ts=》只包含类型信息的类型声明文件,仅用于提供类型信息,为JS提供类型信息
为已有JS文件提供类型说明:如通过ts-loader处理.ts文件
通过命令行编译ts:tsc .\hello.ts --target es6(指定了文件名,将忽略tsconfig.json文件)
创建支持TS的项目命令(React):npx create-react-app 项目名称 --template typescript npm start启动
react-app-env.d.ts:React项目默认的类型声明文件。
三斜线指令:指定依赖的其他类型声明文件,types表示依赖的类型声明文件包的名称。
/// <reference types="react-scripts" />
React是组件化开发模式,React开发主要任务就是写组件(函数组件、class组件)
1.函数组件
type Props = {name:string,age?:number}
// const Hello:FC<Props> = ({name,age}) => (
// <div>你好,我叫{name},今年{age}岁了</div>
// )
Hello.defaultProps = {
age:223//使用了FC,添加默认值
}
//简化
const Hello = ({name,age}:Props) => (
<div>你好,我叫{name},今年{age}岁了</div>
)
const Hello= ({name,age=21}:Props) => (//加默认值
<div>你好,我叫{name},今年{age}岁了</div>
)
const Hello = ({name,age=18}:Props) => {
const onClick = (e:React.MouseEvent<HTMLButtonElement>) => {
console.log("赞",e.currentTarget);
}
const onChange = (e:React.ChangeEvent<HTMLInputElement>) => {
console.log("赞~~~",e.target.value);
}
return (
<div>
你好,我叫{name},今年{age}岁了
<button onClick={onClick}>点赞</button>
<input type="text" onChange={e => {}}/>
</div>
)
}
//在JSX中写事件处理程序(e => {}),然后鼠标放到e上,可以利用TS类型推断来查看事件对象类型
<input type="text" onChange={e => {}}/>
2.class组件
class组件的类型
type State = {count:number}
type Props = {message?:string}
class C1 extends React.Component {}//无Props、Stata
class C2 extends React.Component<Props> {}//有Props、无Stata
class C3 extends React.Component<{},State> {}//无Props、有Stata
class C4 extends React.Component<Props,State> {}//有Props、Stata
class组件的属性和属性默认值
type Props = {name:string,age?:number}
class Hello extends React.Component<Props> {
render() {
const {name,age=18} = this.props//添加属性默认值
return (
<div>你好,我叫{name},今年{age}岁了</div>
)
}
}
class组件状态(state)和事件
type State = {count:number}
class Counter extends React.Component<{},State> {//添加了初始化类型state,下面调用count才有类型提示
state:State = {
count:0
}
handlerClick = () => {
this.setState({
count:this.state.count+1
})
}
render(): React.ReactNode {
return (
<div>
计数器:{this.state.count}
<button onClick={this.handlerClick}>+1</button>
</div>
)
}
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}