一、简介
TypeScript是微软开发的一个开源的编程语言,通过在JavaScript的基础上添加静态类型定义构建而成。TypeScript通过TypeScript编译器或Babel转译为JavaScript代码,可运行在任何浏览器,任何操作系统。
安装ts
// 安装:
npm install -g typescript
// 查看版本
tsc -v
注:编写ts文件,运行ts文件是先编译成js后在执行js,想直接执行ts,可安装ts-node。
二、基础类型
js基础类型分为两种:原始类型和对象类型,其中原始类型包括:数值、字符串、布尔值、null、undefined以及es6中的symbol、biignt。
ts支持与js几乎相同的数据类型。
1、布尔值/字符串/数值
//var [变量名] : [类型] = 值;
let isDone: boolean = false;
const num: number = 1;
const name: string = "猪猪";
//字符串模板依旧能用
const str:string=`${name}`
//声明变量的类型,但没有初始值,变量值会设置为 undefined:
//var [变量名] : [类型];
var test: string;
//声明变量并初始值,但不设置类型,该变量可以是任意类型:
//var [变量名] = 值;
var uname = "abc";
//声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为 undefined:
//var [变量名];
var cord;
(1)num声明变量的类型number之后,后面赋值时,得跟数值,否则则会报错,因为变了num的类型是数值。
(2)如果变量的声明和赋值是同时进行的,ts可以自动对变量进行类型检测。
let str: string;
str = 'hello';
str = 9; // 报错
2、null和undefined
//这两个类型只有 自己
const onlyNull: null = null;
const onlyUndefined: undefined = undefined;
3、数组
//数组泛型,Array<元素类型>
let list: Array<number> = [1, 2, 3];
//string[] 字符串数组
let list: string[];
list = ['a', 'b', 'c'];
//number[] 数值数组
let list: number[] = [1, 2, 3];
4、元组
元组 元组就是固定长度的数组
// 语法:[类型, 类型, 类型]
let list: [string, number];
list = ['hello', 123];
list = ['hello'];// 报错
list = [123, 'hello']; // 顺序不一致
list = ['hello', 123, 11]; // 多余
5、枚举
枚举类型也确实属于对象类型
ts只支持基于数字和字符串的枚举
对于基于数字的枚举类型支持键值对的反向映射 key<=>value
基于字符串的不可以反向映射?当然不可以,就是纯js对象
//
// 枚举
enum Gender{
Male,
Female
}
let i: {name: string, gender: Gender.Male};
i = {
name: '猴子',
gender: Gender.Male
}
console.log(i.gender === Gender.Male); //true
字符串枚举
enum Language{
java="J",
node="N",
php="P",
python="PY"
}
// 打印一下
console.log(Language)
//输出结果
{ java: 'J', node: 'N', php: 'P', python: 'PY' }
// 编译后的js
var Language;
(function (Language) {
Language["java"] = "J";
Language["node"] = "N";
Language["php"] = "P";
Language["python"] = "PY";
})(Language || (Language = {}));
//打印一下
console.log(Language);
数字枚举-默认增长
enum Language{
html,
node,
js,
python
}
//打印一下
console.log(Language)
// 输出结果
{
'0': 'html',
'1': 'node',
'2': 'js',
'3': 'python',
html: 0,
node: 1,
js: 2,
python: 3
}
// 编译后的js
var Language;
(function (Language) {
Language[Language["html"] = 0] = "html";
Language[Language["node"] = 1] = "node";
Language[Language["js"] = 2] = "js";
Language[Language["python"] = 3] = "python";
})(Language || (Language = {}));
//打印一下
console.log(Language);
数字枚举-自定义增长
enum Language{
html=6,
node,
js,
python
}
//打印一下
console.log(Language)
//输出结果
{
'6': 'html',
'7': 'node',
'8': 'js',
'9': 'python',
html: 6,
node: 7,
js: 8,
python: 9
}
// 编译后的js
var Language;
(function (Language) {
Language[Language["html"] = 6] = "html";
Language[Language["node"] = 7] = "node";
Language[Language["js"] = 8] = "js";
Language[Language["python"] = 9] = "python";
})(Language || (Language = {}));
//打印一下
console.log(Language);
异构枚举
enum Person{
age = 18,
gender = 'male'
}
//打印一下
console.log(Person)
// 输出结果
{ '18': 'age', age: 18, gender: 'male' }
// 编译后的js
var Person;
(function (Person) {
Person[Person["age"] = 18] = "age";
Person["gender"] = "male";
})(Person || (Person = {}));
//打印一下
console.log(Person);
6、| 和 &
// | 或 表示可以是字符串或数值
let str: string | number;
str = 1;
console.log(str.length)// 报错
str = 'hello';
console.log(str.length)// 正常运行
str = false; //报错
//& 表示同时
let str2: {name: string} & {age: number};
str2 = {name: '嘿嘿'}; //报错
str2 = {name: '嘿嘿', age: 18};
7、type
// type
//类型的别名
type myType = string;
let k: 1 | 2 | 3 | 4;
let m: myType //m 类型是string
type myType2 = 1 | 2 | 3 | 4;
k = 1;
k = 6;//报错
8、function
//js不考虑参数的类型和个数的
function sum(a, b) {
return a + b;
};
// ts 隐式any
function sum(a: any, b:any):any {
return a + b;
};
function sum2(a:number, b:number): number {
return a + b;
};
let s1 = sum('1', 2); // 12
let s2 = sum(2, 2); // 4
let s3 = sum2(1, 5); // 6
console.log(s1, s2, s3);
9、任意值
任意值(any/unknown)用来表示允许赋值为任意类型
any
//any表示任意类型的值,一个类型使用了any后相当于关闭了ts的类型检测
//在使用ts时 不建议使用any
let a: any
a = 1;
a = 'hello';
a = false;
//如果变量不声明类型,则ts解析器会自动判断变量的类型为any(隐式any)
let b;
b = 1;
b = 'hello';
b = true;
let s: string;
//b的变量是any,它可以赋值给任意变量
s = b;
unknown
//unknown 表示未知类型的值
let c: unknown
c = 1;
c = 'hello';
c = false;
let s: string;
c = 'world';
// 把c赋值给s,也就是把未知变量赋值给其他类型变量会报错
s = c;//报错
//unknown 实际上就是一个类型安全的any
//unknown 不能直接赋值给任意变量
//判断类型
if (typeof c === 'string') {
s = c;
}
//类型断言 用来告诉解析器变量的实际类型
s = c as string;
s = <string> c;
10、void
const u:void=undefined;
const n:void=null;
const age:void=18;//不能将number类型赋值给void
//void 用来表示空 以函数为例 就表示没有返回值的函数
function fn() {
}
function fn2(): void {
}
11、never
//never 表示永远不会返回结果
function fn(): never {
throw new Error('错误的');
}
// 特殊情况下,变量也可以使用never声明,比如一个永远为空的数组。never是任何类型的子类型,也可以赋值给任何类型,但是反过来不可以。(自身除外)
const emptyArr: never[] = []
12、object
//object 表示一个js对象
let a: object;
a = {}
a = function() {}
//{}用来指定对象中可以包含哪些属性
//语法:{属性名:属性值}
//属性名后面加上? 表示属性是可选的
let b: {name: string, age?: number};
b = {};//报错 缺少属性名
b = {name: '阿三'};
b = {name: '李四', age: 12};
// [propName: string]: any 表示任意类型的属性
let c: {name: string, [propName: string]: any};
c = {name: '嘿嘿', age: 12, gender: '男'};
//设置函数结构的类型声明
//语法:(形参:类型, 形参:类型...) => 返回值
let d: (a: number, b: number) => number;
d = function(n1, n2):number {
return n1 + n2;
}
//d = function(n1: string, n2:string): number {
// return 10;
// }
//报错
13、symbol
symbol使用依赖es6编译辅助库 ,tsconfig.json lib[“es6”]
const sym1: symbol = Symbol();
const sym2: symbol = Symbol();
console.log(sym1===sym2); //false
let sym3 = Symbol("key");
let sym4 = Symbol("key");
sym2 === sym3; // false, symbol是唯一的
三、面向对象
1⃣️操作浏览器使用window对象
2⃣️操作网页使用document对象
3⃣️操作控制台使用console对象
万物皆对象
1、类(class)
简介:要创建对象,必须先定义类,所谓的类可以理解为对象的模型。举例说明:可以通过Person类来创建人类的对象,通过Dog类来创建狗的对象等等,不同的类用来创建不同的对象。
Person类
// 使用class关键字定义一个类
/**
* 对象中包含了两个部分:
* 属性
* 方法
*
*/
class Person {
// 定义实例属性
name: string = '猴子';
age: number = 18;
// 静态属性
static num: number = 1;
// readonly
readonly gender: string = '男';
static readonly say: string = 'hello';
// 定义方法
/**
*
* 通过static定义类方法 可以直接用类去调用
*/
syaHello() {
console.log('hello, 大家好');
}
}
const son = new Person();
son.syaHello(); // hello, 大家好
console.log(son);
console.log(son.age, son.name);
console.log(son.age, son.name, son.num); // 报错 没有num这个属性
son.gender = '女'; // 报错 因为readonly是只读的属性 不可修改
Person.say = '1212'; // 使用static开头且只读的属性 不可修改
Person.num; // 使用static开头的属性是静态属性(类属性),可以直接通过类去调用
// 打印结果
Person {name: "猴子", age: 18}
18 "猴子"
dog类
class Dog{
name: string;
age: number;
// constructor 构造函数 构造函数会在对象创建时调用
constructor(name: string, age: number) {
// 在实例方法中 this表示当前的实例
// 可以通过this向新建的对象中添加属性
this.name = name;
this.age = age;
}
brak() {
// 在方法中可以用this表示当前调用方法的对象
//console.log('汪汪汪');
console.log(this.name);
}
}
const dog1 = new Dog('小黑', 10);
const dog2 = new Dog('小白', 11);
console.log(dog1); // {name: "小黑", age: 10}
console.log(dog2); // {name: "小白", age: 11}
dog1.brak(); // 小黑
2、继承
(function() {
// 定义animal类
class Animal {
name: string;
age: number;
// constructor 构造函数 构造函数会在对象创建时调用
constructor(name: string, age: number) {
// 在实例方法中 this表示当前的实例
// 可以通过this向新建的对象中添加属性
this.name = name;
this.age = age;
}
sayHello() {
console.log('动物在叫~');
}
}
/**
* 此时 Animal 被称为为父类 Dog、Cat 被称为子类
* 使用继承后 子类将会拥有父类的所有方法和属性
* 如果在子类中添加了和父类的相同的方法 则子类方法会覆盖父类的方法
*/
// 定义狗的类 继承Animal类
class Dog extends Animal {
run() {
console.log(`${this.name}在跑路~~~`);
}
sayHello() {
console.log('汪汪汪');
}
}
// 定义猫的类 继承Animal类
class Cat extends Animal {
}
const dog = new Dog('小黑', 3);
const cat = new Dog('咪咪', 2);
console.log(dog); // Dog {name: "小黑", age: 3}
dog.sayHello(); // 汪汪汪
dog.run(); // 小黑在跑路~~~
console.log(cat); //Dog {name: "咪咪", age: 2}
cat.sayHello(); // 动物在叫~
cat.run(); // 报错
})();
3、super
// 超类
(function() {
// 定义animal类
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log('动物在叫~');
}
}
class Dog extends Animal {
age: number;
constructor(name: string, age: number) {
// 如果在子类中写了构造函数 在子类函数中必须对父类的构造函数进行调用
super(name); // 调用父类的构造函数
this.age = age;
}
sayHello() {
// 在类的方法中 super表示当前类的父类
//super.sayHello();
console.log('汪汪汪');
}
}
const dog = new Dog('小黑', 2);
dog.sayHello(); // 汪汪汪
console.log(dog); // Dog {name: "小黑", age: 2}
})();
4、抽象类
(function() {
// 定义animal类
/**
* 以abstract开头的类是抽象类
* 抽象类与其他类区别不大 只是不能用来创建对象
* 抽象类就是专门用来继承的类
*
* 抽象类中可以添加抽象方法
*/
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
// 抽象方法使用abstract开头 没有方法体
// 抽象方法只能定义在抽象类中 子类必须对起抽象方法重写
abstract sayHello(): void;
}
class Dog extends Animal {
sayHello() {
// 在类的方法中 super表示当前类的父类
//super.sayHello();
console.log('汪汪汪');
}
}
class Cat extends Animal {
} // 报错 没有实现s对ayHello重写
const dog = new Dog('小黑');
dog.sayHello();
console.log(dog);
//const ani = new Animal(); // 报错 无法创建实例
})();
5、接口
(function() {
// 描述一个对象的类型(别名形式)
type myType = {
name: string,
age: number
}
// type myType = {} // 报错 重复声明
/**
* 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
* 接口也可用来当成类型声明去使用
*
* 接口可重复声明
*/
interface myInterface {
name: string,
age: number
}
interface myInterface {
gender: string
}
const obj: myInterface = {
name: '哈哈哈',
age: 12,
gender: '男'
}
/**
* 接口可以在定义类的时候限制类的结构
* 接口中的所有属性都不能有实际的值
* 接口只定义类的结构 而不考虑实际值
*/
interface myInter {
name: string;
sayHello(): void;
}
/**
* 定义类时,可以使类去实现一个接口
* 实现接口就是使类满足接口的要求
*
*/
class MyClass implements myInter {
name: string;
// 需要写构造函数 给name赋值
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log('大家好');
}
}
const m = new MyClass('嘻嘻');
m.sayHello(); // 大家好
console.log(m); // MyClass {name: "嘻嘻"}
})();
// 编译后的js
"use strict";
(function () {
const obj = {
name: '哈哈哈',
age: 12,
gender: '男'
};
/**
* 定义类时,可以使类去实现一个接口
* 实现接口就是使类满足接口的要求
*
*/
class MyClass {
// 需要写构造函数 给name赋值
constructor(name) {
this.name = name;
}
sayHello() {
console.log('大家好');
}
}
const m = new MyClass('嘻嘻');
m.sayHello();
console.log(m);
})();
6、属性的封装
(function() {
// 定义一个人的类
class Person {
// ts可以在属性前添加修饰符
/**
* public 修饰的属性可以在任意位置访问(修改)默认值
* private 私有属性 只能在对象内部中访问(修改)默认值
* protected 受保护的属性 只能在当前类和当前类的子类访问
*
*/
private _name: string;
age: number;
constructor(name: string, age: number) {
this._name = name;
this.age = age;
}
// 定义方法 获取name属性
// getName() {
// return this._name;
// }
// 定义方法 设置name属性
// setName(value: string) {
// this._name = value;
// }
// ts中设置getter方法的方式
get name() {
console.log('get name() 方法执行了---');
return this._name;
}
set name(value: string) {
this._name = value;
}
}
const per = new Person('猴子', 18);
/**
* 现在属性是在对象中设置的 属性可以任意被修改
* 属性被任意修改会导致对象中的数据变得非常不安全
*
*/
//per._age = 20; // 报错 不能访问
//per.name = '八戒';
//console.log(per); // 输出: {name: "八戒", age: 20}
// per.setName('八戒');
// console.log(per.getName()); // 输出: 八戒
console.log(per.name); // get name() 方法执行了--- 猴子
per.name = '八戒';
console.log(per.name); // 八戒
class A {
protected num: number;
constructor(num: number) {
this.num = num;
}
}
class B extends A {
test() {
console.log(this.num);
}
}
const b = new B(123);
// b.num = 55; // 只能在类“A”及其子类中访问
class C {
// 可以直接将属性定义在构造函数中
constructor(public name: string, public age: number) {
this.name = name;
this.age = age;
}
}
const c = new C('xxx', 123);
console.log(c); // {name: "xxx", age: 123}
})();
7、泛型
// function fn(a: number): number {
// return a;
// }
/**
* 在定义类或函数时 如果遇到类型不明确就可以使用泛型
*
*/
function fn<T>(a: T): T {
return a;
}
// 直接调用具有泛型的函数
const result = fn(123); // 不指定泛型 ts会自动对类型进行推断
const result2 = fn<string>('hello'); // 指定泛型
// 多个
function fn2<T, K>(a: T, b: K): T {
return a;
}
const result3 = fn2<number, string>(123, 'hello');
interface Inter {
length: number
}
function fn3<T extends Inter>(a: T): number {
return a.length;
}
fn3('122'); // 3
//fn3(123); // 类型“number”的参数不能赋给类型“Inter”的参数
fn3({length: 10}); // 10
// 定义类也可以使用泛型
class MyClass<T> {
name: string;
constructor(name: string) {
this.name = name;
}
}
const c = new MyClass<string>('哈哈哈');
//const d = new MyClass<number>(123); // 类型“number”的参数不能赋给类型“string”的参数
四、项目配置
编译选项
1、运行ts文件
tsc test.ts
// 监听当个文件 实时编译
tsc test.ts -w
// 监听全部ts文件
tsc -w
// 编译所有ts文件
tsc
2、tsconfig.json
{
// 编译器选项
"compilerOptions": {
// module使用类型:none,commonjs,amd,system,umd,es6,es2015...
"module": "es2015",
// target 默认es3
"target": "ES2015",
// 开启全部严格模式
"strict": true,
// outDir 输出目录
"outDir": "./dist"
//lib 用来指定使用的库 一般不用写
//lib: [],
// outFile 将全局作用域中的代码合并成一个 用的不多
"outFile": "./dist/app.js",
// 是否编译js 默认false
"allowJs": false,
// 是否检查js 默认false
"checkJs": false,
// 是否清除注释 默认false
"removeComments": false,
// 不生成编译后的文件
//"noEmit": false,
// 当有错误时不生成编译文件
"noEmitOnError": false,
// 设置严格模式
"alwaysStrict": false,
// 不允许隐式any
"noImplicitAny": false,
// 是否检测null
"strictNullChecks": false
},
// 只编译src目录下的ts文件
"include": [
"./src/**/*"
],
// 不需要被编译的ts文件
"exclude": [
"./src/hello/**/*"
]
}
3、检测null
const div = document.getElementById('.div');
if (div !== null) {
...
}
div?.addEventListener('click', function() {
console.log('aaaa');
});
4、项目结构
五、使用webpack打包ts代码
1、安装依赖
// 项目初始化
npm init -y
// 安装工具
npm i -D webpack webpack-cli typescript ts-loader
2、webpack.config.js配置
const path = require('path');
// webpack中的所有配置信息都应该写到module.export中
module.exports = {
// 入口文件
entry: "./src/index.ts",
// 打包文件
output: {
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js"
},
// 打包使用的模块
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
exclude: "/node_modules/"
}
]
}
};
3、tsconfig.js配置
{
"compilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true
}
}
4、html-webpack-plugin配置
安装:
npm i html-webpack-plugin -D
配置:
//引入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 配置webpack插件
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
]
5、热更新
安装:
npm i webpack-dev-server -D
配置:
"dev": "webpack serve --open 'google chrome'"
运行:
npm run dev
6、clean-webpack-plugin配置
安装:
npm i clean-webpack-plugin -D
配置:
// 引入
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
//使用
new CleanWebpackPlugin()
7、模块引入
在a.ts中暴露一个变量,然后在index.ts去引入,但是在打包过程中会报错,引入webpack不能识别引入的模块,因此需要去设置,需要在webpack.config.js中添加resolve,用来设置模块引入文件的扩展名。
配置:
resolve: {
extensions: [
'.ts',
'.js'
]
}
a.ts
export const h: string = '123';
index.ts
import { h } from './a';
console.log(h);
8、babel
npm i @babel/core @babel/preset-env babel-loader core-js -D
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。