一.TypeScript的介绍
TypeScript是由微软开发的自由和开源的编程语言,是JavaScript的超集,包含了JavaScript的所有元素,可以载入JS运行代码,并扩展了JS的语法。
二.TypeScript vs JavaScript
-
TypeScript 从核心语言方面和类概念的模塑方面对 JavaScript 对象模型进行扩展。
-
JavaScript 代码可以在无需任何修改的情况下与 TypeScript 一同工作,同时可以使用编译器将 TypeScript 代码转换为 JavaScript(注:TypeScript 代码需要被编译成 JavaScript 代码浏览器不支持TypeScript)。
-
TypeScript 通过类型注解提供编译时的静态类型检查。
-
TypeScript 中的数据要求带有明确的类型,JavaScript不要求。
let isDone: boolean = false; // TypeScript
let isDone = false; // JavaScript
- TypeScript 为函数提供了缺省参数值(xxx? : yyy)。
interface Person {
name: string,
age: number,
sex?: string
}
function sayHello(person: Person): any {
return `我是${person.name},今年${person.age}岁`
}
let user = { name: '小葡萄', age: 12 }
console.log(sayHello(user));
- TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中
class Greeter {
greeting: string;
constructor (message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
三.TypeScript的优点
1. 静态输入
静态类型化是一种功能,可以在开发人员编写脚本时检测错误。查找并修复错误是当今开发团队的迫切需求。有了这项功能,就会允许开发人员编写更健壮的代码并对其进行维护,以便使得代码质量更好、更清晰。
2. 帮助更好地重构代码
有时为了改进开发项目,需要对代码库进行小的增量更改。这些小小的变化可能会产生严重的、意想不到的后果,因此有必要撤销这些变化。ts的静态检查特性会帮助找出代码中有错误的部分。
3. 更好的协作
当发开大型项目时,会有许多开发人员,此时乱码和错误的机也会增加。类型安全是一种在编码期间检测错误的功能,而不是在编译项目时检测错误。这为开发团队创建了一个更高效的编码和调试过程。
4. vscode等IDE的提示更加智能
js是一门动态弱类型解释语言,变量声明后可以改变类型,而且类型需要在运行时才能确定。而ts的报错提示是在编译时,不是在运行时。所以使用ts带来的静态类型检查等特性将使得IDE的提示更加完善。
四.TypeScript的使用
官方文档: www.tslang.cn/
1. 基本类型和复合类型
1.1 基本类型:string、number、Array、null、undefined、元组 、枚举enum、Any、void、never
- string(字符串类型)
// 字符串,单双引都行
let name: string = "bob";
let sentence: string = `Hello, my name is ${ name }`.
- number(数字类型)
// 数字,二、八、十六进制都支持
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
- Array(数组类型)
// 数组,第二种方式是使用数组泛型,Array<元素类型>:
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
- null
let n: null = null; // 表示一个空对象的引用
- undefined
let u: undefined = undefined; // 是一个没有设置值的变量
- 元组
// 元组:允许表示一个已知元素数量和数组,各个元素的类型不必相同
let x = [string,number]
x=['10',14] //ok
x=[10,14] // error
x[0].substr(1) // ok // 截取字符串
x[1].substr(1) // error
x[3]='world' // ok
- enum(枚举)
// 枚举:类型是对JavaScript标准数据类型的一个补充。像C#等其它语言一样,使用枚举类型可以为
一组数值赋予友好的名字
// 默认情况从0开始为元素编号,也可手动为1开始
enum Color {Red,Green,Blue}
let c: Color = Color.Green
enum Color {Red=1,Green,Blur}
let colorName: string = Color[2] // Green
- Any(任何类型)
// Any: 接入第三库、用户输入时运用
let list: Any[] =['1',2,false]
- Void(没有任何的类型)
// void: 没有任何的类型(当一个函数没有返回值时,TS中需要定义函数的返回值类型)
let list: void = undefined || null
- never(永不存在的值的类型)
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
1.2 复合类型: set 和 map
set 指的是一个无序的、无重复元素的集合。
type Size = 'small' | 'default' | 'big' | 'large';
map指的是没有重复键的键值对。
interface IA {
a: string
b: number
}
复合类型之间的转换
// 1.map转成set
type IAKeys = keyof IA; // 'a'| 'b'
type IAValues = IA[keyof IA]; // // string | number
// 2.set转map
type sizeMap = {
[k in Size]: number
}
// 等价于
type sizeMap = {
small: number,
default: number,
big: number,
large: number
}
拓展:位运算符
1001 | 1010 = 1011 // 合并1
1001 & 1010 = 1000 // 只保留共有1
在TypeScript中的使用
interface IA {
a: string
b: number
}
type TB = {
b: number
c: number[]
}
type TC = IA | TB; // TC类型的变量的键只需包含ab或bc即可,当然也可以abc都有
type TD = IA & TB; // TD类型的变量的键必需包含abc
1.3 类型断言 (两种方式:<>和as)
这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。
let somethig : any = 'This is an apple';
let length: number = (something as string).length
let length: number = (<string>something).length
2.泛型
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
2.1 泛型的方法
我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了类型变量,它是一种特殊的变量,只用于表示类型而不是值
1、泛型函数
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
// 调用方式
let output = identity<string>('hello world')
let output = identity('hello world') //第二种调用方式可省略类型参数,因为编译器会根据传入参数来自动识别对应的类型。
2、泛型接口
interface Goods<T>{
id:number;
title: string;
size: T;
}
let apple:Goods<string> = {id:1,title: '苹果', size: 'large'};
let shoes:Goods<number> = {id:1,title: '苹果', size: 43};
2.2 泛型与Any
/ 方法一:带有any参数的方法
function any_func(arg: any): any { // 当传入的数据不是数组、字符串带有length属性的参数时,会抛出异常
console.log(arg.length);
return arg;
}
// 方法二:Array泛型方法
function array_func<T>(arg: Array<T>): Array<T> { // 传入的是Array的泛型,肯定有length属性
console.log(arg.length);
return arg;
}
3.interface(接口) vs type(类型别名))
相同点:
- 都是描述一个对象或者一个函数
interface User {
name: string,
age: number
}
type User = { name: string,age: number}
interface setUser {(name: string, age: number):void}
type SetUser = (name: string, age: number): void;
- 都可以extends扩展,只是用法不同
// interface extends interface
interface Name {
name: string;
}
interface User extends Name {
age: number;
}
// type extends type
type Name = {
name: string;
}
type User = Name & { age: number };
//interface extends type
type Name = {
name: string;
}
interface User extends Name {
age: number;
}
//type extends interface
class Name {
name:string
}
type User= Name & {age:number};
不同点:
- type 可以声明基本类型别名,联合类型,元组等类型要,要扩展已有type需要创建新type,不可以重名。支持更复杂的类型操作
type Tuple = [number, string];
const a: Tuple = [2, 'sir'];
type Size = 'small' | 'default' | 'big' | number;
const b: Size = 24;
// 联合类型
interface Dog {
wong();
}
interface Cat {
miao();
}
type Pet = Dog | Cat
// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div
//还有其他的操作
type StringOrNumber = string | number;
type Text = string | { text: string };
type NameLookup = Dictionary<string, Person>;
type Callback<T> = (data: T) => void;
type Pair<T> = [T, T];
type Coordinates = Pair<number>;
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };
- interface 能够声明合并
// 接口的非函数
interface User {
name: string
age: number
}
interface User {
sex: string
}
/*
User 接口为 {
name: string
age: number
sex: string
}
*/
// 函数成员,每个同名函数声明都会被当成这个函数的一个重载。 同时需要注意,当接口 A与后来的接口 A合并时,后面的接口具有更高的优先级
interface Cloner {
clone(animal: Animal): Animal;
}
interface Cloner {
clone(animal: Sheep): Sheep;
}
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
4. 实现与继承(extends vs implements)
extends 很明显就是ES6里面的类继承。
implements实现。与C#或Java里接口的基本作用一样, TypeScript 也能够用它来明确的强制一个类去符合某种契约
interface IDeveloper {
name: string;
age?: number;
}
class dev implements IDeveloper { // ok
name = 'Alex';
age = 20;
}
class dev2 implements IDeveloper { // ok
name = 'Alex';
}
class dev3 implements IDeveloper {
name = 'Alex';
age = '9'; // Error
}
而 extends 是继承父类,两者其实可以混着用
class A extends B implements C,D,E
5.声明文件与命名空间: declare 和 namespace
5.1 声明文件
declare 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能
-
declare var 声明全局变量
-
declare function 声明全局方法
-
declare class 声明全局类
-
declare enum 声明全局枚举类型
-
declare global 扩展全局变量
-
declare module 扩展模块
5.2 命名空间 (命名空间一个最明确的目的就是解决重名问题) “内部模块”现在称做“命名空间”
TypeScript 中命名空间使用 namespace 来定义,语法格式如下:
namespace SomeNameSpaceName {
export interface ISomeInterfaceName { }
export class SomeClassName { }
}
如果我们需要在外部可以调用 SomeNameSpaceName 中的类和接口,则需要在类和接口添加 export 关键字
要在另外一个命名空间调用语法格式为:
SomeNameSpaceName.SomeClassName;
如果一个命名空间在一个单独的 TypeScript 文件中,则应使用三斜杠 /// 引用它,语法格式如下:
/// <reference path = "SomeFileName.ts" />
6.访问修饰符: private 、 public 、 protected
- 默认为 public
- 当成员被标记为 private 时,它就不能在声明它的类的外部访问,比如:
class Animal {
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
let a = new Animal('Cat').name; //错误,‘name'是私有的
- protected 和 private 类似,但是, protected 成员在派生类中可以访问
class Animal {
protected name: string;
constructor(theName: string) {
this.name = theName;
}
}
class Rhino extends Animal {
constructor() {
super('Rhino');
}
getName() {
console.log(this.name) //此处的name就是Animal类中的name
}
}
五.React + Ts 的结合使用
1.创建React 和 TypeScript
npm install -g create-react-app
create-react-app my-app --scripts-version=react-scripts-ts
react-scripts-ts是一系列适配器,它利用标准的create-react-app工程管道并把TypeScript混入进来。此时的工程结构应如下所示:
my-app/
├─ .gitignore
├─ node_modules/
├─ public/
├─ src/
│ └─ ...
├─ package.json
├─ tsconfig.json
└─ tslint.json
注意: tsconfig.json包含了工程里TypeScript特定的配置选项。(可以tsc --init生成自己的TS配置文件)
- 添加状态管理
//安装redux 和 react-redux
npm install -S redux react-redux @types/react-redux
六.Vue + Ts 的结合使用
vue create my-app
有两种书写方式
- 第一种: Vue.extend
export default Vue.extend({
props: ["name", "initialEnthusiasm"],
data() {
return {
enthusiasm: this.initialEnthusiasm
};
},
methods: {
increment() {
this.enthusiasm++;
},
decrement() {
if (this.enthusiasm > 1) {
this.enthusiasm--;
}
}
},
computed: {
exclamationMarks(): string {
return this.enthusiasm;
}
}
});
- 第二种:vue-property-decorator 和 vuex-class 提供的装饰器
vue-property-decorator 的装饰器:
- @Prop
- @PropSync
- @Provide
- @Model
- @Watch
- @Inject
- @Provide
- @Emit
- @Component (provided byvue-class-component)
- Mixins (the helper function named mixins provided by vue-class-component)
vuex-class 的装饰器:
- @State
- @Getter
- @Action
- @Mutation
import { Vue, Component, Prop } from "vue-property-decorator";
@Component
export default class HelloDecorator extends Vue {
// prop属性
@Prop() name!: string;
@Prop() initialEnthusiasm!: number;
enthusiasm = this.initialEnthusiasm;
// 方法
increment() {
this.enthusiasm++;
}
decrement() {
if (this.enthusiasm > 1) {
this.enthusiasm--;
}
}
// 计算属性
get exclamationMarks(): number {
return this.enthusiasm;
}