let age:number=20;
let myName:string='xsx'
let isLoading:boolean=false;
let a:null=null;
let b:undefined=undefined;
let s:symbol=Symbol();
let numbers:number[]=[1,2,3];
let numbers2:Array=[1,2,3];
let arr:(number|string)[]=['1','2',1,2];
function add(num1:number,num2:number):number{
return num1+num2;
}
const add2=(num1:number,num2:number):number=>{
return num1+num2;
}
function mySlice(start?:number,end?:number):void{
console.log(`起始索引${start},结束索引${end}`);
}
mySlice();
mySlice(1);
mySlice(1,2);
let person:{name:string;age:number;sayHi():void;greet(name:string):void}={
name:'jack',
age:19,
sayHi(){},
greet(name){},
}
let person2:{
name:string
age:number
sayHi:()=>void
greet(name:string):void
}={
name:'jack',
age:19,
sayHi(){},
greet(name){},
}
function myAxios(config:{url:string;method?:string}){
console.log(config);
}
interface IPerson{
name:string
age:number
sayHi():void
}
let person3:IPerson={
name:'jack',
age:19,
sayHi(){}
}
interface Point2D{
x:number
y:number
}
interface Point3D extends Point2D{
z:number
}
let d2:Point2D={
x:1,
y:2
}
let d3:Point3D={
x:1,
y:2,
z:3
}
type TPerson={
name:string
age:number
sayHi():void
}
let person4:TPerson={
name:'jack',
age:19,
sayHi(){}
}
//如果不知道是什么类型的,可以在控制台选中元素,输入console.dir($0)查看。
const aLink = document.getElementById('link')
//接口只能给对象起别名,而类型别名可以给任意类型起别名
type NumStr = number | string;
const str2:'Hello TS' = 'Hello TS'
let age2:18=18
function changeDirection(direction:'up'|'down'|'left'|'right'){
console.log(direction);
}
changeDirection('up');
//对于数字枚举存在自增长行为,如果你不设置数值,则会从0依次增长。
enum Direction{Up,Down,Left,Right}
enum Direction2{Up=10,Down=22,Left=65,Right=1025}
function changeDirection2(direction:Direction){
console.log(direction);
}
changeDirection2(Direction.Up)
enum Direction3{
Up='Up',
Down='Down',
Left='Left',
Right='Right',
}
/*
不推荐使用any类型,它会让TypeScript变成AnyScript,会失去TypeScript类型保护的优势。
*/
let objans:any={x:0};
objans();
//上述代码执行错误
//typeof只能查询变量或属性的类型,无法查询其他形式的类型
let p={x:1,y:2};
function formatPoint(point:{x:number,y:number}){}
formatPoint(p);
function formatPoint2(point:typeof p){}
formatPoint2(p);
class Persons{
ages!:number
gender='男'
}
const per=new Persons();
class Persons{
ages!:number
gender='男'
constructor(ages:number,gender:string){
this.ages=ages;
this.gender=gender
}
}
const per=new Persons(18,'女');
class Point{
x=10
y=10
scale(n:number):void{
this.x*=n;
this.y*=n;
}
}
const poi=new Point();
poi.scale(10);
class Animal{
move(){
console.log('移动');
}
}
class Dog extends Animal{
bark(){
console.log('汪');
}
}
const dog=new Dog();
interface Singalbe{
sing():void
}
class Persons2 implements Singalbe{
sing(): void {
console.log('sign');
}
}
class Animal{
public move(){
console.log('移动');
}
}
//仅对声明的所有类和子类中(非实例对象)可见。
class Animal{
protected move(){
console.log('移动');
}
}
class Dog extends Animal{
bark(){
console.log('汪');
this.move();
}
}
//只在当前类中可见,对实例对象以及子类都不可见
class Animal{
private move(){
console.log('移动');
}
}
class Dog extends Animal{
bark(){
console.log('汪');
this.move();//报错
}
}
/*
用来防止在构造函数之外对属性进行赋值,可以在声明时和constructor里面进行赋值
只可以修饰属性,不能修饰方法
下面代码中如果:number没有写,则age会变成字面量类型,如果再在constructor中改变值会报错。
接口或{}表示的对象类型,也可以使用readonly
*/
class Persons3{
readonly age:number=18
constructor(){
this.age=age;
}
setAge(){
this.age=20//报错
}
}
interface readi{
readonly age:18
}
let obji:{readonly age:number}={
age:18
}
TypeScript采用的是结构化类型系统,也叫做duck typing(鸭子类型),如果两个对象具有相同的形状,则认为它们属于同一类型。
在Java、c#中,它们是标明类型系统,不会根据具有相同形状来判度。
class Pointl{x!:number;y!:number}
class pointl2D{x!:number;y!:number}
const pl:Pointl=new pointl2D();
//在TypeScript看来Pointl和pointl2D是同一类型
对于对象类型来说,y的成员至少与x相同,则x兼容y(成员多的可以兼容少的)。
class pointl2D{x!:number;y!:number}
class pointl3D{x!:number;y!:number;z!:number}
const ppl:Pointl=new pointl3D();
interface Int1{
x:number
y:number
}
interface Int2{
x:number
y:number
}
interface Int3{
x:number
y:number
z:number
}
let inte1:Int1={x:1,y:2};
let inte2:Int2={x:1,y:2};
let inte3:Int3={x:1,y:2,z:3};
inte1=inte2;
inte2=inte1;
inte1=inte3;
inte3=inte1;//报错
interface ic1{
x:number
y:number
}
class ic2{
x!:number
y!:number
}
let ict:ic1=new ic2();
函数之间兼容性要考虑三个方面。
type F1=(a:number)=>void
type F2=(a:number,b:number)=>void
let f1:F1=(a)=>{};
let f2:F2=(a,b)=>{};
f2=f1;
f1=f2;//报错
type F1=(a:number,b:string)=>void
type F2=(a:number,b:number)=>void
let f1:F1=(a,b)=>{};
let f2:F2=(a,b)=>{};
f2=f1;//报错
interface ic1{
x:number
y:number
}
class ic2{
x!:number
y!:number
}
type F1=(a:number,b:ic1)=>void
type F2=(a:number,b:ic2)=>void
let f1:F1=(a,b)=>{};
let f2:F2=(a,b)=>{};
f2=f1;
type F5=()=>string;
type F6=()=>string;
let f5:F5=()=>'1';
let f6:F6=()=>'1';
f6=f5;
- 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的(和对象类型一直)
type F7=()=>{name:string}
type F8=()=>{name:string;age:number}
let f7:F7=()=>{return{
name:'111'
}};
let f8:F8=()=>{return{
name:'111',
age:10
}};
f7=f8;
f8=f7;//报错
交叉类型&功能类似于接口的继承,用于组合多个类型为一个类型(常用于对象类型)
interface jio1{name:string}
interface jio2{age:number}
type Jio = jio1&jio2;
let obj:Jio={
name:'111',
age:10
}
interface A{
fn(a:number):string
}
interface B extends A{//报错,类型不兼容
fn(a:string):string
}
interface A{
fn(a:number):string
}
interface B{
fn(a:string):string
}
type C = A & B
对于C我们可以理解为一下代码
interface A{
fn(a:number):string
}
interface B{
fn(a:string):number
}
type C = A & B
class cc implements C{
fn(a: number): string;
fn(a: string): number;
fn(a: unknown): string | number {
return ''
}
}
泛型可以在保护类型安全的前提下,让函数等多种类型一起工作,从而实现复用。例:实现一个函数,输入什么数据就返回什么数据。
function id(value:Type):Type{
return value
}
id(10);
id(10);//如果编译器推断的类型不准确,我们必须在括号前声明类型
id(10);//报错
//Type也可以写成别的合法名称
function id2(value:Type):Type{
return value.length//报错
}
添加泛型约束
function ida(value:type[]):type[]{
return value.length
}
interface ILength{length:number};
function ide(value:type):type{
console.log(value.length);
return value
}
ide([]);
ide('');
ide({length:0,name:'0'});
keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
function getProp(obj:Type,key:Key){
return obj[key];
}
let objkey={name:'11'};
getProp(objkey,'name');
getProp(objkey,'age');//报错
getProp([],0);
getProp(0,'toString');
interface IdFunc{
id:(value:Type)=>Type
ids:()=>Type[]
}
let func1:IdFunc={
id(value) {
return value
},
ids() {
return []
},
}
可以点击去数组的forEach源码来查看。
在React的class组件中的基类Component就是泛型类,不同的组件有不同的props和state。
interface IState{count:number}
interface IProps{maxLength:number}
class InputCount extends React.Component{
state:IState{
const:0
}
render(){
return {this.props.maxLength}
}
}
创建泛型类
class Generic{
defaultVlaue!:NumType
add!:(x:NumType,y:NumType)=>NumType
}
const myGen = new Generic()
myGen.defaultVlaue=10
TypaScript内置了一些常用的工具类型,来简化TypeScript中的一些常见操作。
interface PropsP{
id:string
children:number[]
}
type PartialProps = Partial
class classProps implements PartialProps{
id!: string;
}
interface PropsR{
id:string
children:number[]
}
type ReadonlyProps = Readonly
let rp:ReadonlyProps={
id:'1',
children:[]
}
rp.id='2'//报错
interface PropsPi{
id:string
children:number[]
}
type PickProps = Pick
let pp:PickProps={
id:'1',
children:[]//报错
}
//PickProps类型中只有id,所以会报错。
type RecordObj=Record<'a'|'b'|'c',string[]>
let objRec:RecordObj={
a:['1'],
b:['1'],
c:['1']
}
当无法确认对象中有哪些属性或者说对象中可以出现任意多个属性,此时就用到索引签名类型了。
interface AnyObject{
[Key:string]:number
}
let anyobj:AnyObject={
a:1,
'b':2,
}
//Key只是一个占位符,可以换成任意合法的变量名称。
type PropKeys='x'|'y'|'z'
type Type1={x:number;y:number;z:number}
type Type2={[Key in PropKeys]:number}//映射
//Key只是一个占位符,可以换成任意合法的变量名称
type Props={a:number,b:string,c:boolean}
type Type3={[Key in keyof Props]:number}
type Partials={
[P in keyof T]?:T[P]
}
type ParText={a:number;b:string;c:boolean}
type part=Partials
type Propsi={a:number;b:number;c:number}
type Tyepi=Propsi['a'];
type Propsi={a:number;b:number;c:number}
type Tyepi2=Propsi['a'|'b'];
type Tyepi3=Propsi[keyof Propsi];
今天几乎所有的JavaScript应用都会引入许多第三方库来完成任务需求。这些第三方库不管是否是用TS编写的,最终都要编译成JS代码,才能发布给开发者使用。我们知道是TS提供了类型,才有了代码提示和类型保护等机制。但在项目开发中使用第三方库时,你会发现它们几乎都有相应的TS类型,这些类型是怎么来的呢?类型声明文件:用来为已存在的JS库提供类型信息。这样在TS项目中使用这些库时,就像用TS-样,都会有代码提示、类型保护等机制了。
创建支持TypeScript的项目的命令:npx create-react-app 项目名。在已有项目中使用TypeScript
相对于非TypeScript项目,目录结构主要由以下三个变化。
{
"compilerOptions": {
"target": "es6",//生成代码的语言版本
"lib": [//指定要包含在编译中的library
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,//允许ts编译器编译js文件
"skipLibCheck": true,//跳过声明文件的类型检查
"esModuleInterop": true,//es模块互操作,屏蔽ESModule和CommonJS之间的差异
"allowSyntheticDefaultImports": true,//允许使用import
"strict": true,//开启严格模式
"forceConsistentCasingInFileNames": true,//对文件名称强制区分大小写
"noFallthroughCasesInSwitch": true,//为switch语句启用报错报告
"module": "esnext",//生成代码的模块化标准
"moduleResolution": "node",//模块解析(查找)策略
"resolveJsonModule": true,//允许导入扩展名为.json的模块
"isolatedModules": true,//是否将没有import/export的文件规为旧(全局而非模块化)脚本文件
"noEmit": true,//编译时不生产任何文件(只进行类型检查)
"jsx": "react-jsx"//将指定jsx编译成什么形式
},
"include": [//指定允许ts处理的目录
"src"
]
}
在不使用TypeScript时,可以使用prop-types库,为React组件提供类型检查。在TypeScript项目中,推荐使用TypeScript或Flow实现组件类型校验(代替prop-types)
//组件和属性类型
import React,{FC} from 'react';
type Props={name:string;age?:number}
const Hello:FC=({name,age})=>(
你好,我叫:{name},我{age}岁了
)
const Hello2=({name,age}:Props)=>(
你好,我叫:{name},我{age}岁了
)
//属性默认值
import React,{FC} from 'react';
const Hello:FC=({name,age})=>(
你好,我叫:{name},我{age}岁了
)
Hello.defaultProps={
age:18
}
const Hello2=({name,age=18}:Props)=>(
你好,我叫:{name},我{age}岁了
)
//事件与事件对象
import React from 'react'
export default function Test() {
function onclick(e:React.MouseEvent){
console.log(e.currentTarget);
}
function onchange(e:React.ChangeEvent){
console.log(e.target);
}
return (
)
}
type State = { count: number }
type Props = { message? :string }
class C1 extends React.Component }// 无props、state
class C2 extends React.Component {}//有props,无state
class C3 extends React.Component<{}, State> }//无props, 有state
class C4 extends React.Component 年} //有props、state
import React, { Component } from 'react'
type Props = { name:string;age?:number }
export default class TestClass extends Component {
static defaultProps:Partial={
age:14
}
render() {
const {name,age=18} = this.props
return (
你好,我叫:{name},我{age}岁了
)
}
}
import React, { Component } from 'react'
type State = { count:number }
export default class TestClass extends Component<{},State> {
state:State={
count:0
}
add=()=>{
this.setState({
count:this.state.count+1
})
}
render() {
return (
{this.state.count}
)
}
}