Wscats / node-tutorial

:relaxed:Some of the node tutorial -《Node学习笔记》
https://github.com/Wscats/node-tutorial
555 stars 94 forks source link

TypeScript #21

Open Wscats opened 7 years ago

Wscats commented 7 years ago

安装

NPM全局安装TypeScript模块

npm install typescript -g

编译

TypeScript文件均以ts为后缀的文件,所以可以命名xxx.ts文件,并用命令行执行tsc xxx.tx

tsc xxx.ts

类型

TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用

布尔值

最基本的数据类型就是简单的true/false值,在JavaScript和TypeScript里叫做boolean

let bool: boolean = false;
//=>
var bool = false;

数字

和JavaScript一样,TypeScript里的所有数字都是浮点数。 这些浮点数的类型是 number。 除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量

let num: number = 9;
//=>
var num = 9;

字符串

JavaScript程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用 string表示文本数据类型。 和JavaScript一样,可以使用双引号( ")或单引号(')表示字符串

let str: string = `Wscats ${bool?"is":"isn't"} ${num} years old`;
//=>
var str = "Wscats " + (bool ? "is" : "isn't") + " " + num + " years old";

数组

TypeScript像JavaScript一样可以操作数组元素。 有两种方式可以定义数组。 可以在元素类型后面接上 [],表示由此类型元素组成的一个数组

let arr: string[] = ["Oaoafly", "Corrine", "Eno"];
let arr: Array<number> = [1, 2, 3];
//=>
var arr = ["Oaoafly", "Corrine", "Eno"];

元组

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string和number类型的元组

let tuple: [string, number];
tuple = ['hello', 10];
//=>
var tuple;
tuple = ['hello', 10];

Any

有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any类型来标记这些变量

let notSure: any = 8;
let list: any[] = [false, 9, "Wscats"];
//=>
var notSure = 8;
var list = [false, 9, "Wscats"];

Void

某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void

function warning(): void {
    console.log("This is my warning message");
}
//=>
function warning() {
    console.log("This is my warning message");
}

声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null

let warning: void = undefined;

Null和Undefined

TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null。 和void相似,它们的本身的类型用处不是很大

let u: undefined = undefined;
let n: null = null;

默认情况下null和undefined是所有类型的子类型。 就是说你可以把 null和undefined赋值给number类型的变量

Never

never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时

never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}

接口

类型检查器会查看getInfo的调用。 getInfo有一个参数,并要求这个对象参数有一个名为name类型为string的属性。 需要注意的是,我们传入的对象参数实际上会包含很多属性,但是编译器只会检查那些必需的属性是否存在,并且其类型是否匹配

interface objValue {
    name: string;
}

function getInfo(obj: objValue) {
    console.log(obj.name);
}

let myObj = {
    age: 10,
    name: "Wscats"
};
getInfo(myObj);

接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型

interface addValue {
    sum: number,
    good: string
}
function add(x: number, y: number, good: string): addValue {
    return {
        sum: x + y,
        good: good
    };
}
//=>相当于
function add(x: number, y: number, good: string): {
    sum: number,
    good: string
} {
    return {
        sum: x + y,
        good: good
    };
}
//=>相当于
interface Fn {
    (x: number, y: number, good: string): {
        sum: number,
        good: string
    };
}

const add: Fn = function (x, y, good) {
    return {
        sum: x + y,
        good: good
    };
}

接口还可以继承

class Control {
    age: number;
}
interface AnimalType extends Control {
    name: String;
    eat(food: String);
}
// 或者
interface PersonType {
    height: number
}
interface AnimalType extends PersonType {
    name: String;
    eat(food: String);
}
const god: AnimalType = {
    name: 'god',
    height: 18,
    eat() { }
}

传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。 从ECMAScript 2015,也就是ECMAScript 6开始,JavaScript程序员将能够使用基于类的面向对象的方式。 使用TypeScript,我们允许开发者现在就使用这些特性,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet(): string {
        return "Hello, " + this.greeting;
    }
}
let greeter = new Greeter("world");
//=>
var Greeter = /** @class */ (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
}());
var greeter = new Greeter("world");

类类型 && implements

配合interfaceclass的类型进行约束

// implements 就是:实现的意思。
// 类实现接口本质上也是一样的,即类遵循接口的约束,接口里面写了多少个函数、参数,实现的类里面也要写相同的函数、参数。
; (() => {
    interface AnimalType {
        name: String;
        eat(food: String);
    }
    // Cat类需要遵循AnimalType的类型
    class Cat implements AnimalType {
        constructor(name: string) {
            this.name = name
        }
        name: String;
        eat(food) {
            console.log(this.age)
            console.log(this.name + food);
        }
    }

    class Lion extends Cat implements AnimalType {
        constructor(name: string) {
            super(name)
        }
    }
    let Tom = new Cat('Tom');
    let King = new Lion('King');
    console.log(Tom.name);
    King.eat('Apple');
})()

函数

函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义 行为的地方。 TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用

let f = (x: number, y: number): number => {
    console.log(1)
    return x + y
}
f(8, 9)
//=>
var f = function (x, y) {
    console.log(1);
    return x + y;
};
f(8, 9);

可选参数和默认参数

f(8, 9, "mobile") f(8, 9) => var f = function (x, y, good, shop) { if (shop === void 0) { shop = "gz"; } console.log(good, shop); return x + y; }; f(8, 9, "mobile"); f(8, 9);

接口也同样支持可选参数,但不支持默认参数
```ts
interface SquareConfig {
    color?: string;
    width?: number;
}

interface SquareResult {
    width?: number;
    color?: string;
    area?: number;
}

function createSquare(config: SquareConfig): SquareResult {
    return {
        color: config.color,
        area: 14
    }
}

let mySquare = createSquare({ color: "red" });
Wscats commented 5 years ago

Vue + Typescript

data

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;
  message: string = 'Hello!';
}
</script>

methods

都要放在export default class HelloWorld extends Vue里面而非@Component里面

export default class HelloWorld extends Vue {
  @Prop() private msg!: string;
  message: string = 'Hello!';
  // 组件方法也可以直接声明为实例的方法
  greet (): void {
    window.alert(this.message)
  }
}
</script>

因为

import Vue from 'vue'
const Component = Vue.extend({
  // 类型推断已启用
})

const Component = {
  // 这里不会有类型推断,
  // 因为TypeScript不能确认这是Vue组件的选项
}
Wscats commented 5 years ago

React + Typescript

使用 TSX 我们可以这样写,后缀记得为.tsx,与js的区别是,使用ts开发过程中需要先定义一个接口,规范数据类型,通过泛型传入到类中

import * as React from 'react'
interface IProps {
  color: string,
  size?: string,
}
interface IState {
  count: number,
}
class App extends React.Component<IProps, IState> {
  public state = {
    count: 1,
  }
  public render () {
    return (
      <div>Hello world</div>
    )
  }
}

TypeScript 可以对 JSX 进行解析,充分利用其本身的静态检查功能,使用泛型进行 Props、 State 的类型定义。定义后在使用 this.state 和 this.props 时可以在编辑器中获得更好的智能提示,并且会对类型进行检查。

在这里可以看到 Component 这个泛型类,

P 代表 Props 的类型
S 代表 State 的类型

Component 泛型类在接收到 P , S 这两个泛型变量后,将只读属性 props 的类型声明为交叉类型 Readonly<{ children?: ReactNode }> & Readonly<P>; 使其支持 children 以及我们声明的 color 、 size 。

通过泛型的类型别名 Readonly 将 props 的所有属性都设置为只读属性

class Component<P, S> {
    readonly props: Readonly<{ children?: ReactNode }> & Readonly<P>;
    state: Readonly<S>;
}

事件

常见事件类型

ClipboardEvent<T = Element> 剪贴板事件对象
DragEvent<T = Element> 拖拽事件对象
ChangeEvent<T = Element> Change 事件对象
KeyboardEvent<T = Element> 键盘事件对象
MouseEvent<T = Element> 鼠标事件对象
TouchEvent<T = Element> 触摸事件对象
WheelEvent<T = Element> 滚轮事件对象
AnimationEvent<T = Element> 动画事件对象
TransitionEvent<T = Element> 过渡事件对象

例子

import React from 'react';
export type ColumnCount = 1 | 2 | 3 | 4 | 6 | 8 | 12 | 24;
export interface ButtonProps {
    // ?为非必须传入
    text?: string; //字符串
    bordered?: boolean; //布尔值
    size?: number; //数字
    date?: Date; //时间
    reg?: RegExp; //正则
    v?: void; // 空型
    n?: null; // null
    u?: undefined; // undefined
    any?: any; //any
    numArr?: number[]; //数字数组
    readonly id?: number; //readonly 定义只读属性 能在创建的时候被赋值
    // 默认传入函数格式
    onClick?: (key: string) => void; // 函数
    onError?: () => boolean;
    // 限制 props.type 的值只可以是字符串 normal、default、danger
    type?: 'normal' | 'default' | 'danger';
    sizeType?: 'sm' | 'xm' | 'lg';
    style?: React.CSSProperties; //样式
    children?: React.ReactNode; //节点
    onChange?: (affixed?: boolean) => void;
    /** 设置 Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
    target?: () => Window | HTMLElement | null;
    // 无?必须传入
    color: string;
    ColumnCount?: ColumnCount;
    abc?: 'abc',
    html?: HTMLElement
    pow?(x: number, y: number): number | string;
}
// 可省略export 直接写interface
export interface ButtonState {
    name: string,
    age: number
}

export interface data {
    skill: [string]
}

const ListStyles: React.CSSProperties = {
    width: "50px",
    marginRight: "10px",
    border: "1px solid red",
    textAlign: "center",
    cursor: "pointer"
}

const data = {
    skill: ['ps', 'js', 'css']
}

export default class Button extends React.Component<ButtonProps, ButtonState>{
    name: string = 'jing';
    static defaultProps = {
        defalutCls: 'keep-btn',
        loading: false,
        type: 'default',
        size: 'xm'
    }

    constructor(props: ButtonProps) {
        super(props);
        this.state = {
            name: 'yao',
            age: 18
        }
    }
    testClick(): void {
        this.setState({
            name: 'jing'
        })
    }
    showText(age: number): string {
        return `name:${this.state.name} age:${age}`
    }
    render() {
        console.log(this)
        return <div>
            <p>{this.state.name}</p>
            <p>{this.showText(this.state.age)}</p>
            <button onClick={this.testClick.bind(this)}>按钮</button>
            <ul>
                {data.skill.map((item: string, index: number) => <li style={ListStyles} key={index}>{item}</li>)}
            </ul>
            <div>{this.props.children}</div>
        </div>
    }
}

type

利用type关键词

export type ColumnCount = 1 | 2 | 3 | 4 | 6 | 8 | 12 | 24;
export interface ButtonProps {
  ColumnCount?: ColumnCount;
  // 等价于 ColumnCount?: 1 | 2 | 3 | 4 | 6 | 8 | 12 | 24;
}

参考文档

Wscats commented 5 years ago

微信小程序

由于 setDatadata 都被声明为可选项,使用时需要加上 !this.setData!({})this.data!

interface InterObj {
  age?: number
}
Page({
  data: {
    skill: ['ps', 'js', 'css'] as string[],
    name: 'yao' as string | number,
    obj: {
      age: 18
    } as InterObj
  },
  plus(a: number, b: number): number {
    return a + b
  },
  changeName(): void {
    this.setData!({
      name: 'jing'
    })
  },
  onLoad() {
    this.plus(1, 2)
    this.changeName()
  }
})

或者可以这么写


interface IndexPageData {
  motto: string;
  userInfo: {
    name?: string
  };
}

class IndexPage {
  public data: IndexPageData = {
    motto: 'Hello World',
    userInfo: {}
  }

  public bindViewTap(): void {
    wx.navigateTo({
      url: '../logs/logs'
    });
  }

  public onLoad(): void {
    console.log('onLoad');
  }
}

Page(new IndexPage());