TypeScript入门教程: http://ts.xcatliu.com/basics/
动态类型是指在运行时才会进行类型检查
静态类型是指编译阶段就能确定每个变量的类型
1)使用typescript编译
npm install -g typescript
2)配置tsconfig.json
该文件一定保存在项目根目录,是编译ts文件的编译选项
修改tsconfig.json
执行编译(即使编译出错也会生成js文件)
tsc -h // 查看帮助
tsc -v // 查看版本
tsc --init // 在根目录生成tsconfig.js
3)修改tsconfig.json
"outDir":"./dist" //编译后的js存储在dist文件夹中
执行编译(即使编译后出错也会生成js文件)
!!!特别注意,后面不要指定文件
tsc // 会编辑所有ts文件为js文件
4)使用ts-node直接编译并执行,
npm i ts-node -g
npm i @types/node -g
//运行
ts-node hello.ts
{
// 字符串
let str: string = "5"
//数值
let num: number = 6
//布尔值
let flag: boolean = true
// null
let userInfo: null;
//undefined
let data: undefined;
}
let a: any = 1
a = "b"
console.log(a.a) //undefined
let v;
v = 2;
v = 'nihao'
console.log(v);
TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论
let a = "7" // 推测出是string 就必须是string类型
a = 8 //报错
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查
let a;
a = 7
a = 'c'
联合类型(Union Types)表示取值可以为多种类型中的一种。
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
下方代码编译报错:
function getLength(something: string | number): number {
//return something.length; // 报错 number 没有长度
return something.toString(); // 不报错
}
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven'; // 推断成string
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7; // 推断成number
console.log(myFavoriteNumber.length); // 编译时报错
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。
用于对「对象的形状(Shape)」进行描述。
{
interface Cat{
nickname: string,
age:number
}
let cat : Cat = {
nickname: 'a',
age: 7
}
}
interface Cat{
nickname: string,
age:number,
skin?: string
}
interface Person {
readonly id: number;
name: string;
age?: number;
}
let tom: Person = {
id: 89757, // 创建时第一次赋值不被约束
name: 'Tom',
gender: 'male'
};
tom.id = 9527; // 此时会报错,受到只读的限制
在 TypeScript 中,数组类型有多种定义方式,比较灵活。
let fibonacci: number[] = [1, 1, 2, 3, 5];
{
let userList : Array<string> = ['a','b']
let List : Array<number> = [1,5,9]
}
{
interface IUser {
[index:number]: string
}
let userList :IUser = ['a','b']
}
{
let num: any[] = [1,'a',false]
}
一个函数有输入和输出,要在 TypeScript 中对其进行约束
需要把输入(参数)和输出(返回值)都考虑到,其中函数声明的类型定义较简单
{
// 没有返回值
function fun1(x: string, y: number): void {
console.log(x + y)
}
fun1('1', 2)
// 有返回值
function fun2(x: string, y: number): string {
return x + y
}
fun2('1', 2)
}
输入多余的(或者少于要求的)参数,是不被允许的
{
//myFun1通过类型推断为右边函数的类型
let myFun1 = function(x: string,y:number):string {
return x + y
}
//手动定义myFun2的函数类型
let myFun2 : (x: string,y:number)=>string = function(x: string,y:number):string {
return x + y
}
}
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
与接口中的可选属性类似,我们用 ? 表示可选的参数
{
let myFun = function(x: string,y?:number):string{
return x + y
}
myFun('1')
}
可选参数必须接在必需参数后面
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现
{
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
}
类型断言(Type Assertion)可以用来手动指定一个值的类型。
{
interface Cat{
name: string,
run(): void
}
interface Fish{
name: string,
swim(): void
}
function getName(animal: Cat | Fish){
//报错,因为只能访问Cat和Fish的共同属性
if (typeof animal.swim === 'function'){
return true
}
}
}
而有时候,我们确实需要在还不确定类型的时候就访问其中一个类型特有的属性或方法
此时可以使用类型断言as,将 animal 断言成 Fish
interface Cat {
name: string,
run(): void
}
interface Fish {
name: string,
swim(): void
}
const getAnimalName = function (animal: Cat | Fish): string {
if (typeof (animal as Cat).run === 'function') {
// animal.run() //报错
(animal as Cat).run()
} else {
(animal as Fish).swim()
}
return animal.name
}
let cat: Cat = {
name: 'lucy',
run() {
console.log('run');
}
}
let fish: Fish = {
name: 'nancy',
swim() {
console.log('swim');
}
}
// let catname:string = getAnimalName(cat)
let animalname: string = getAnimalName(fish)
// ----------------------------ECMAScript 的内置对象
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
//-------------------------DOM 和 BOM 的内置对象
// DOM 和 BOM 提供的内置对象有:
// Document、HTMLElement、Event、NodeList 等。
// TypeScript 中会经常用到这些类型:
let body: HTMLElement = document.body;
let div: HTMLElement = document.querySelectorAll('div')[0];
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
});
类型别名用来给一个类型起个新名字
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
字符串字面量类型用来约束取值只能是某几个字符串中的一个
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'
数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象
// 固定类型的数组
let score:number[] = [23,45,67];
// 任意类型的数组
let data:any[] = ['1','3',12];
// 元组,合并不同类型的对象,且都可以一一对应
let tom:[string,number] = ['Tom',24];
let tom:[string,number] = ['Tom',24,6]; // 报错
let tom:[string,number] = ['Tom','25']; // 报错
{
enum Direct {Up,Right,Down,Left}
console.log(Direct.Up); //0
console.log(Direct[0]); //'Up'
// 鼠标移动案例
enum Direct {
Up = 38,
Right = 39,
Down = 40,
Left = 37
}
// console.log(Direct.Up); // 0
// console.log(Direct[0]); //'Up'
const move = (status: number): void => {
const { Up, Right, Down,Left} = Direct;
switch (status) {
case Up:
console.log('向上移动');
break;
case Right:
console.log('向右移动');
break;
case Down:
console.log('向下移动');
break;
case Left:
console.log('向左移动');
break;
default:
console.log(new Error('No have wrong code!'));
}
};
move(38);
}
class Animal {
// 属性要提前定义类型
public name:string;
constructor(name:string) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`;
}
}
let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用
class Animal {
static isAnimal(a) {
return a instanceof Animal;
}
}
let a = new Animal('Jack');
Animal.isAnimal(a); // true
a.isAnimal(a); // TypeError: a.isAnimal is not a function
class Animal {
public name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom
class Animal {
private name;
public constructor(name) {
this.name = name;
}
say(){
console.log(this.name)
}
}
let a = new Animal('Jack');
console.log(a.name);
a.name = 'Tom';
class Animal {
protected name;
public constructor(name) {
this.name = name;
}
}
class Cat extends Animal {
constructor(name) {
super(name);
console.log(this.name);
}
}
interface Alarm {
alert(): void;
}
class Door {
}
class SecurityDoor extends Door implements Alarm {
alert() {
console.log('SecurityDoor alert');
}
}
class Car implements Alarm {
alert() {
console.log('Car alert');
}
}
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
这段代码编译不会报错,但是一个显而易见的缺陷是,它并没有准确的定义返回值的类型
function createArray(length: number, value: any): Array<any> {
let result = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
我们预期的是,数组中每一项都应该是输入的 value 的类型。
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
createArray<number>(3, 'x'); // ['x', 'x', 'x']
在函数名后添加了 ,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array 中即可使用了。