TypeScript 简称『TS』,是微软开发的一个开源的编程语言。
TS 主要有如下几个特点:
完全兼容 JavaScript,是 JavaScript 的超集
引入类型系统,可以尽早的定位错误位置, 帮助提升开发效率
let obj = {a:1,b:2....}
obj = ()=>{}
console.log(obj.a);
TypeScript 在社区的流行度越来越高,它非常适用于一些大型项目,
也非常适用于一些基础库,极大地帮助我们提升了开发效率和体验。
全局安装:npm i typescript -g
或者yarn add global typescript
npm root -g
: 查看全局包安装的路径使用命令检查是否安装成功以及查看包的版本:tsc -v
tsconfig.json
【只要在脚手架里面就需要进行配置文件初始化】tsc --init
创建一个 JS 文件 『hello.ts』
let str: string = 'hello world';
console.log(str);
//参数类型 //返回值类型
function add(a: number, b:number):number{
return a + b;
}
console.log(add(1, 100));
注意:ts
本身不能直接运行, 必须要转为 js
才可以运行
命令行运行,这里的后缀是 ts
tsc hello.ts
命令行运行,这里的后缀是 js
node hello.js
项目中可以借助 webpack 打包工具来直接处理 ts 文件。操作步骤如下:
创建目录结构
- src
- index.ts
- public
- index.html
安装 webpack 以及相关的工具包
npm init (-y)
npm i webpack webpack-cli webpack-dev-server ts-loader html-webpack-plugin typescript -D
根目录下添加配置文件
webpack.prod.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.ts',
output: {
path: resolve(__dirname, '../dist'),
filename: 'js/bundle.js'
},
mode: 'production',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
// inject: 'body'
})
],
}
webpack.dev.js
const configProd = require('./webpack.prod');
module.exports = {
...configProd,
devServer: {
host: '127.0.0.1',
port: 3000,
open: true
},
mode: 'development',
}
config目录下创建 『tsconfig.json』
tsc --init
如果想要沿用之前的npm start,需要在package.json配置文件中添加一条配置
"build": "npx webpack --config ./config/webpack.prod.js",
"start": "npx webpack serve --config ./config/webpack.dev.js"
友情提示:当如果有多个ts文件需要进行导入的话,最终都会变成全局变量来解析,容易发生重名,可以导出{}
TypeScript 支持与 JavaScript 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用
TS 支持的变量类型如下:
类型 | 描述 |
---|---|
boolean | 限制为布尔类型, true 或者 false |
number | 限制为任意的数字。 二进制,八进制,十进制,十六进制均可 |
string | 限制为任意的字符串。单引号,双引号,反引号均可 |
字面量 | 限制为某个字面量的值 |
any | 限制为任意类型 |
void | 限制为 undefined, 一般用来限制函数的返回值 |
never | 限制函数没有返回值 |
object | 限制为对象类型 |
array | 限制为数组类型 |
tuple 元组 | 限制为固定长度与类型的数组 |
enum 枚举 | 限制为枚举的数据 |
最基本的数据类型就是简单的 true/false 值,在JavaScript 和 TypeScript 里叫做 boolean
(其它语言中也一样)。
let bool: boolean = true;
console.log(bool);
let bool1: boolean;
console.log(bool1); //这种写法变量没有赋值,则值就是undefined,但是b变量的数据类型要求是boolean所以报错
let bool2: boolean = true;
bool2 = false;
console.log(bool2)
let bool3: boolean = true;
bool3 = 100; //不能将类型“number”分配给类型“boolean”
console.log(bool3)
const a: boolean = true;
a = false; //无法分配到 "a" ,因为它是常数。
console.log(a)
除了支持十进制和十六进制字面量,也支持二进制和八进制字面量。
let num0: number = 0b0110101;//二进制数值
console.log(num0); //53
let num: number = 100; //十进制数值
console.log(num); //100
let num1: number = 0o56; //八进制数值
console.log(num1); //46
let num2: number = 0x5F; //十六进制数值
console.log(num2);//95
let num3: number = Infinity;//无穷大
console.log(num3)//Infinity
let num4: number = 34.56 //浮点数
console.log(num4);//34.56
let num5: number = -56; //负数
console.log(num5); //-56
JavaScript 程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用 string
表示文本数据类型。 和 JavaScript 一样,可以使用双引号("
)或单引号('
)表示字符串。
let name:string = 'tom'
name = 'jack'
// name = 12 // error
let age:number = 12
const info = `My name is ${name}, I am ${age} years old!`
TS 允许限制某个变量为固定的某个值,用的很少
let x: 123 = 123; //变量的值只能是123
// x = 456; //不能修改成其他值,有点类似于const常量
console.log(x)
let y: 'hello';
y = 'hello';
console.log(y)
any 类型允许变量的值为任意类型, 并且可以进行任意类型的赋值
let a: any;
a = 100;
a = 'hello';
a = {};
a = [];
console.log(a)
void 限制值为 undefined
,可以限制变量的值或者是函数的返回值,更多的时候是限制函数的返回值
let v: void;
console.log(v); //undefined
function fn1(): void {
return;
}
console.log(fn1()); //undefined
let fn = (): void => {
return;
}
console.log(fn()); //undefined
永远不会有值的一种类型(必须存在无法达到的终点),基本不用
function fn(): never {
throw new Error('出错啦');
}
fn();
object 限制类型为对象,
let obj: object = {name:'张三',age:23};
console.log(obj)
用的比较多
TypeScript 像 JavaScript 一样可以操作数组元素。 不过数组中元素值的类型都是一样的.
有两种方式可以定义数组。
第一种,可以在元素类型后面接上[]
,表示由此类型元素组成的一个数组:
第二种, 方式是使用数组泛型,Array<元素类型>
:
//1) 直接咋变量后面添加:类型[] 要求元素中每一个都是限制的这个类型
let arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(arr)
let arr1: string[] = ['张三', '李四', '王五'];
//操作数组
arr1.push('赵六');
console.log(arr1)
//2) 泛型 Array<约束类型>
let arr2: Array<Boolean> = [true, false, true];
console.log(arr2)
console.log(arr2.join('/'))
元组(Tuple)类型允许表示一个已知元素数量和类型的数组
,各元素的类型不必相同
,用的较少
let persons: [string, number] = ['王二麻子', 18];
console.log(persons)
let arr: [object, Array<number>] = [{ a: 100 }, [10, 20, 30]];
console.log(arr)
console.log(arr[0]);
console.log(arr[1]);
let arr1: [string, number][] = [
['张三', 18],
['李四', 19],
['王五', 20]
]
console.log(arr1)
枚举(enum)类型是对 JavaScript 标准数据类型的一个补充。
使用枚举类型可以为一组数值赋予友好的名字
。
一般用于组织一些有相似之处的常量,让这些常量更规范、更统一。
enum gender {
nan = '男',
nv = '女',
screct = '保密'
}
//11.创建一个枚举类型的变量
let sex: object = {
sex: gender.nv
}
console.log(sex)
联合类型(Union Types)表示取值可以为多种类型中的一种。
let x: string | number;
x = 'hello world'
x = 100;
// x = true; //其他类型不允许
console.log(x);
类型断言(Type Assertion)可以告诉编译器,“相信我,我知道自己在干什么,别报错,出了事我负责”。
let x: string | number;
x = 34.55666;
// console.log(x.toFixed(2));
//方法一:as
// console.log((x as number).toFixed(2))
//方法二:类型
console.log((<number>x).toFixed(2))
TS 会在没有明确的指定类型的时候推测出一个类型。主要有下面两种情况
let t = 123;
// t = true; //已经赋值了的变量规定为number,再次修改也必须为number
t = 23;
console.log(t)
let y;
y = 100;
y = 'hello';
y = true;
console.log(y)
TypeScript 为 JavaScript 函数添加了额外的功能
TypeScript 可以为参数与返回值设置类型,代码示例如下:
//1、普通函数
//参数添加类型
function fun(a: number, b: number) {
console.log(a + b)
}
fun(1, 2);
//添加函数返回值类型
function fun1(x: string, y: number): string {
return x + y;
}
console.log(fun1('11', 20));
//2、箭头函数
let fun2 = (a: number, b: number): number => {
return a + b;
}
console.log(fun2(12, 34));
TypeScript 能够根据返回语句自动推断出返回值类型,因此我们通常省略返回值的类型。
现在我们已经为函数指定了类型,下面让我们写出函数的完整类型。
//4、声明函数
let fun5: (a: number, b: number) => number = (x: number, y: number): number => {
return x + y;
}
console.log(fun5(10, 20));
TypeScript 默认要求函数实参数量要与形参的数量保持一致,不过可以使用『 ?: 』设置参数为可选参数
let fn = (a: number, b: number, c?: number) => {
console.log(a);
console.log(b);
console.log(c);
}
fn(1, 2)
TypeScript 与 JavaScript 一样,允许为参数设置默认值
let fun6: (a: number, b: number, c?: number) => number = (x: number, y: number, z: number = 40): number => {
return x + y + z;
}
console.log(fun6(10, 20, 100));
针对不定个数参数的函数时,我们可以使用 ES6 提供的 rest 参数来处理。不过在 TypeScript 中需要设置类型。
//剩余参数
function add(a: number, b: number, ...args: number[]){
//求和
args = [a, b, ...args];
return args.reduce((prev: number, current: number) => {
return prev + current;
})
}
TS 中的类与 JS 的类使用方式类似, 不过增加了对属性与方法的类型限制
这里的类型限制跟变量和函数的类型限制是非常相似的
class Student {
//属性
name: string = '张三'
age: number;
height: number;
constructor(age: number, height: number) {
this.age = age;
this.height = height;
}
//方法
play(): void {
console.log('玩')
}
study(): string {
return '学习'
}
}
let s1 = new Student(10, 180);
console.log(s1)
//调用方法
s1.play();
console.log(s1.study())
TypeScript 继承语法与 JavaScript 完全相同
class Person {
address: string = '北京市'
name: string;
age: number;
height: number;
constructor(name: string, age: number, height: number) {
this.name = name;
this.age = age;
this.height = height;
}
eat(address: string, who: string): string {
return '吃'
}
}
class Student extends Person {
weight: number;
constructor(name: string, age: number, height: number, weight: number) {
super(name, age, height);
this.weight = weight;
}
//对父类的方法进行重写
eat(address: string, who: string): string {
return this.name + '在' + address + '和' + who + '吃饭';
}
}
let s1 = new Student('张三', 23, 180, 120);
console.log(s1)
//调用方法
console.log(s1.eat('街边', '女朋友'))
一些面向对象编程语言(Java, C++)中都有成员修饰符特性,TypeScript 也引入成员修饰符。
成员修饰符有三个:
这些修饰符实现了对象成员的访问控制
自身类 | 子类 | 类外部 | |
---|---|---|---|
public | √ | √ | √ |
protected | √ | √ | X |
private | √ | X | X |
//继承
class Father {
public name: string;
protected age: number;
private salary: number;
constructor(name: string, age: number, salary: number){
this.name = name;
this.age = age;
this.salary = salary;
}
info(){
console.log(this.name);
console.log(this.age);
console.log(this.salary);
}
}
class Son extends Father{
constructor(name: string, age: number, salary: number){
super(name, age, salary);
}
checkSalary(){
console.log(this.name);
console.log(this.age);
}
}
let xue = new Son('xue', 26, 16000);
console.log(xue.name);
TypeScript 中引入了接口,用来限制对象的结构与类型。代码示例:
//声明接口
interface BoyFriend{
name: string;
age: number;
}
//声明对象 满足接口结构与类型要求
let zhangsan: BoyFriend = {
name: '张三',
age: 18,
}
console.log(zhangsan);
上述代码中,对象的属性不能多, 也不能少,属性值的类型也必须满足接口的要求
如果某些属性不是固定的,只是某些条件下存在,可以使用可选属性配置
interface BoyFriend{
name: string,
age: number,
car ?: string
}
这样设置之后,对象中的 car 属性就不是必须的属性
如果一些属性的值不允许修改,则可以使用『readonly』标志,这样一旦发现被修改,立即报错
interface BoyFriend{
readonly id: number,
name: string,
age: number,
car ?: string
}
这样设置后, 如果修改 id 属性, 编译器就会报错
readonly 与 const 的功能很像,不过 readonly 是限制对象的属性, const 是限制变量的值
接口除了可以限制属性类型之外,也可以对对象的方法进行限制
interface BoyFriend{
readonly id: number,
name: string,
age: number,
car ?: string,
//必选
cook: () => void,
//可选
skeep?: () => void
}
//当如果接口中的定义的方法为可选的方法的时候,在对象实现的时候并且调用那么程序将认为这个可选的方法属性有可能存在,也有可能不存在
//如果存在则是接口中的定义的方法值,如果不存在则为undefiend
//解决办法有两个
//1、修改tsconfig.json文件中的strict严格模式为false,如果修改了配置文件需要[重启脚手架],那么将不在进行严格的检查 但是这种形式会忽略到一些其他的语法检查 [不太推荐]
//2、只要调用可选方法,在调用的()的前面添加一个!,程序就会认为该方法skeep一定是存在 [推荐]
console.log(obj.speek!()) */
obj.skeep!()
该接口要求对象必须要有 cook 方法且返回结果必须为 undefined
之前接口只能限制单个对象的结构与类型,接口与类结合之后,可以约束一类对象的结构与类型
interface BoyFriend {
name: string;
age: number;
car ?: string;
cook: () => void
}
class Person implements BoyFriend{
name: string;
age: number;
car: string;
constructor(name: string, age: number, car: string){
this.name = name;
this.age = age;
this.car = car;
}
cook(){
console.log('我可以做蛋炒饭~~');
}
}
一个类可以同时实现多个接口,类必须要包含对应的属性和方法才能通过编译
语法:class 类名 implements 接口名称1,接口名称2…{}
interface BoyFriend{
readonly id: number,
name: string,
age: number,
car ?: string,
cook: () => void
}
interface Staff{
name: string
age: number
programTS: () => void;
}
class Person implements BoyFriend, Staff{
//对象属性
id:number
name: string
age: number
//构造方法
constructor(id: number, name: string, age: number){
this.id = id;
this.name = name;
this.age = age;
}
//对象的方法
cook(): void{
console.log('做方便面。。。');
}
//对象方法
programTS(){
console.log('我可以编写 TypeScript');
}
}
当接口中出现重复结构时,可以对公共部分进行抽离,然后通过继承来简化代码
interface BasicInfo{
name: string,
age: number,
}
interface BoyFriend extends BasicInfo{
readonly id: number,
car ?: string,
cook: () => void
}
interface Staff extends BasicInfo{
programTS: () => void;
}
泛型(generic)指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
下面创建一个函数, 实现功能: 根据指定的数量 count
和数据 value
, 创建一个包含 count
个 value
的数组 不用泛型的话,这个函数可能是下面这样:
function createArray (count:number,value:any):any[]{
const arr:any[] = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr01 = createArray(3,'hello')
const arr02 = createArray(3,100)
console.log(arr01[0].split('')) //运行不报错,但编码时没有提示
console.log(arr02[0].toFixed(3)) //运行不报错,但编码时没有提示
console.log(arr02[0].split('')) //运行报错,但编码时没有提示错误
function createArray <P>(count:number,value:P):P[]{
const arr:P[]= []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr03 = createArray<string>(3,'hello')
const arr04 = createArray<number>(3,100)
console.log(arr03[0].split(''))
console.log(arr04[0].toFixed(1))
console.log(arr04[0].split('')) //error 类型“number”上不存在属性“split”
一个函数可以定义多个泛型参数
function createArray <T,P> (a: T, b: P): [T, P] {
return [a, b]
}
const result = createArray<string, number>('abc', 123)
console.log(result[0].length)
console.log(result[1].toFixed())
在定义接口时, 为接口中的属性或方法定义泛型类型
在使用接口时, 再指定具体的泛型类型
// 声明一个接口
interface Response<T>{
status: number,
headers: object,
data: T,
}
interface Stu{
id: number,
name: string,
age: number,
}
interface Book{
id: number,
title: string,
price: number
}
//一个对象
let response: Response<Book> = {
status: 200,
headers: {},
data: {
id: 1,
title: '西游记',
price: 28
}
}
let response2: Response<Stu> = {
status: 200,
headers: {},
data: {
id: 1,
name: 'xx',
age: 19
}
}
在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
//泛型类
class Container<T>{
//用于储存数据
store: T[];
constructor(store: T[]){
this.store = store;
}
}
let books = new Container<Book>([{id: 1, title: '西游记', price: 10}, {id: 2, title: '三国演义', price: 25}])
如果我们直接对一个泛型参数取 length
属性, 会报错, 因为这个泛型根本就不知道它有这个属性
// 没有泛型约束
function fn <T>(x: T): void {
// console.log(x.length) // error
}
我们可以使用泛型约束来实现
interface Lengthwise {
length: number;
}
// 指定泛型约束
function fn2 <T extends Lengthwise>(x: T): void {
console.log(x.length)
}
fn2('abc')
// fn2(123) // error number没有length属性
可以使用 type 关键字声明类型
//1. 类型声明
type Computer = {
brand: string,
price: number
}
interface Person{
name: string,
age: number
}
type Persons = Person[];
//2. 获取某个变量的类型
let me: string = '尚硅谷';
type SchoolType = typeof me;
//3. 获取接口键名的联合类型
function getInfo(obj: Person, key: keyof Person): void{
obj[key]
}
//4. 获取函数的返回值类型
type retType = ReturnType<typeof getInfo>;
//5. 获取函数参数类型. 返回类型为『元组』类型
type paramsType = Parameters<typeof getInfo>
JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。
内置对象是指根据标准在全局作用域(Global)上存在的对象。
这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。
let b: Boolean = new Boolean(1)
let n: Number = new Number(true)
let s: String = new String('abc')
let d: Date = new Date()
let r: RegExp = /^1/
let e: Error = new Error('error message')
b = true
// let bb: boolean = new Boolean(2) // error
const div: HTMLElement = document.getElementById('test')
const divs: NodeList = document.querySelectorAll('div')
document.addEventListener('click', (event: MouseEvent) => {
console.dir(event.target)
})
# 创建项目
create-react-app react-ts --template typescript
# 进入目录
cd react-ts
# 启动服务
npm start
import ReactDOM from 'react-dom/client'
import App from './App'
// 创建应用根对象, 指定页面容器元素
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
// 渲染App组件
root.render(<App />)
文档: https://zh-hans.reactjs.org/docs/strict-mode.html
在 React 严格模式下, 一些不好的语法使用会在浏览器控制台显示相应的错误提示
只在开发模式下起作用, 生产环境打包时会自动去除
开启 React 严格模式
<React.StrictMode>
<App/>
</React.StrictMode>
建议在开发阶段, 关闭这个严格模式, 该模式会导致每次输出内容, 弹出两次, 影响调试