0%

基础类型

https://www.tslang.cn/docs/handbook/basic-types.html

布尔值

let isDone: boolean = false;

数字

let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;

字符串

let name: string = "bob";

let name: string = `Gene`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.
I'll be ${ age + 1 } years old next month.`;

数组

let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];

元组 Tuple

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

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型

console.log(x[5].toString()); // OK, 'string' 和 'number' 都有 toString

x[6] = true; // Error, 布尔不是(string | number)类型

枚举

enum Color {Red, Green, Blue} // Red 默认为 0
let c: Color = Color.Green;
enum Color {Red = 1, Green, Blue} // Green -> 2 一次类推
let c: Color = Color.Green;
console.log(colorName);  // 显示'Green'因为上面代码里它的值是2
enum Color {Red = 1, Green = 2, Blue = 4} // 手动赋值
let c: Color = Color.Green;

Any

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
let list: any[] = [1, true, "free"];
list[1] = 100;

Void

function warnUser(): void {
    console.log("This is my warning message");
}
let unusable: void = undefined;

Null 和 Undefined

// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = 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) {
    }
}

Object

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

类型断言

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

变量声明

https://www.tslang.cn/docs/handbook/variable-declarations.html

var a = 10;

let hello = "Hello!";

const numLivesForCat = 9;

解构

解构数组

let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2
// swap variables
[first, second] = [second, first];
function f([first, second]: [number, number]) {
    console.log(first);
    console.log(second);
}
f(input);
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]
let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1
let [, second, , fourth] = [1, 2, 3, 4];

对象解构

let o = {
    a: "foo",
    b: 12,
    c: "bar"
};
let { a, b } = o;
({ a, b } = { a: "baz", b: 101 });
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;

属性重命名

let { a: newName1, b: newName2 } = o;
// === 等价 ===
let newName1 = o.a;
let newName2 = o.b;
let {a, b}: {a: string, b: number} = o;

默认值

function keepWholeObject(wholeObject: { a: string, b?: number }) {
    let { a, b = 1001 } = wholeObject;
}

函数声明

type C = { a: string, b?: number }
function f({ a, b }: C): void {
    // ...
}
function f({ a="", b=0 } = {}): void {
    // ...
}
f();
function f({ a, b = 0 } = { a: "" }): void {
    // ...
}
f({ a: "yes" }); // ok, default b = 0
f(); // ok, default to {a: ""}, which then defaults b = 0
f({}); // error, 'a' is required if you supply an argument

展开

let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5]; // [0, 1, 2, 3, 4, 5]
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" }; // { food: "rich", price: "$$", ambiance: "noisy" }
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { food: "rich", ...defaults }; // defaults里的food属性会重写food: "rich"

对象展开还有其它一些意想不到的限制。 首先,它仅包含对象 自身的可枚举属性。 大体上是说当你展开一个对象实例时,你会丢失其方法:

class C {
  p = 12;
  m() {
  }
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!

接口

https://www.tslang.cn/docs/handbook/interfaces.html

TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

变形

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

可选属性

interface SquareConfig {
  color?: string; // 带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。
  width?: number;
}

/*
可选属性的优势
    一、可以对可能存在的属性进行预定义
    二、可以捕获引用了不存在的属性时的错误。 
比如,我们故意将 createSquare里的color属性名拼错,就会得到一个错误提示
*/
function createSquare(config: SquareConfig): {color: string; area: number} {
  let newSquare = {color: "white", area: 100};
  if (config.clor) {
    // Error: Property 'clor' does not exist on type 'SquareConfig'
    newSquare.color = config.clor;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({color: "black"});

只读属性

interface Point {
    /*
    一些对象属性只能在对象刚刚创建的时候修改其值。
    你可以在属性名前用 readonly来指定只读属性
    */
    readonly x: number;
    readonly y: number;
}
// TypeScript具有ReadonlyArray<T>类型,它与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
a = ro as number[]; // ok

额外的属性检查

函数类型

interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
// 函数的参数名不需要与接口里定义的名字相匹配
// mySearch = function(source: string, subString: string) {
mySearch = function(src: string, sub: string): boolean {
  let result = src.search(sub);
  return result > -1;
}

可索引的类型

class Animal {
    name: string;
}
class Dog extends Animal {
    breed: string;
}

// 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
interface NotOkay {
    // TypeScript支持两种索引签名:字符串和数字。
    // 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。
    [x: number]: Animal;
    [x: string]: Dog;
}
interface ReadonlyStringArray {
    // 可以将索引签名设置为只读,这样就防止了给索引赋值。
    readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!

类类型

实现接口

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}

// 接口描述了类的公共部分,而不是公共和私有两部分。 它不会帮你检查类是否具有某些私有成员。
class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

类静态部分与实例部分的区别

// 定义了两个接口, ClockConstructor为构造函数所用和ClockInterface为实例方法所用。 
interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface; // 静态部分
}
interface ClockInterface {
    tick(); // 实例部分
}
// 定义一个构造函数 createClock,它用传入的类型创建实例。
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
    // 因为当一个类实现了一个接口时,只对其实例部分进行类型检查。
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep");
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("tick tock");
    }
}
// 因为createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 7, 32)里,会检查AnalogClock是否符合构造函数签名。
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

继承接口

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

混合类型

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

function getCounter(): Counter {
    let counter = <Counter>function (start: number) { };
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}

// 一个对象可以同时做为函数和对象使用,并带有额外的属性。
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

接口继承类

  • 当接口继承了一个类类型时,它会继承类的成员但不包括其实现。
  • 接口同样会继承到类的private和protected成员。
  • 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。
class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl {
    select() { }
}

class Location {
}

作用域关键字

  • public 无限制访问(默认)
  • private 仅在本类中访问
  • protected 在派生类中仍然可以访问
// 抽象类
abstract class Department {
	
    constructor(public name: string) {
    }

    printName(): void {
        console.log('Department name: ' + this.name);
    }

    abstract printMeeting(): void; // 必须在派生类中实现
}

// 继承
class AccountingDepartment extends Department {
    // 静态属性,可以通过AccountingDepartment.test来访问
	static test = 'test';
    
    private _departmentName: string;
    
    // 只读属性必须在声明时或构造函数里被初始化
    protected readonly departmentLogo: string = "★";
    
    constructor() {
        super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
    }
    
    // 参数属性test2
    constructor(private readonly test2: string) {
        super('Accounting and Auditing');
    }
    
    // 存取器
    get departmentName(): string {
        return this._departmentName
    }
    set departmentName(newName: string) {
        this._departmentName = newName;
    }

    // 实现抽象方法
    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }

    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}

let department: Department; // 允许创建一个对抽象类型的引用
department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
department.generateReports(); // 错误: 方法在声明的抽象类中不存在

同心圆

测试地址:https://echarts.apache.org/examples/zh/editor.html?c=pie-simple

var data = [{
        name: "博士及以上",
        value: 0.2
    },
    {
        name: "硕士及以上",
        value: 0.3
    },
    {
        name: "本科及以上",
        value: 1
    },
    {
        name: "高中",
        value: 0.1
    },
    {
        name: "初中及以下",
        value: 0.1
    },
    {
        name: "其他",
        value: 0.8
    }
];
var dataStyle = { 
    normal: {
        label: {show:false},
        labelLine: {show:false},
        shadowBlur: 40,
        shadowColor: 'rgba(40, 40, 40, 0.5)',
    }
};
var placeHolderStyle = {
    normal : {
        color: 'rgba(0,0,0,0)',
        label: {show:false},
        labelLine: {show:false}
    },
    emphasis : {
        color: 'rgba(0,0,0,0)'
    }
};
var legendData=[];
function getData(data) {
    var sortData=data.sort((a,b)=>{
        return b.value-a.value
    });
    var res = [];
    for (let i = 0; i < sortData.length; i++) {
        legendData.push(sortData[i].name);
        res.push({
            type: 'pie',
            clockWise: false, //顺时加载
            hoverAnimation: false, //鼠标移入变大
            radius: [200 - i * 20, 220 - i * 20],       //radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
            itemStyle: dataStyle,
            data: [{
                value: sortData[i].value,
                name: sortData[i].name
            }, {
                value: 1 - sortData[i].value,
                name:'invisible',
                itemStyle: placeHolderStyle,
            }]
        });
    }
    return res;
}
option = {
   backgroundColor: '#f4f2e3',
    color: ['#85b6b2', '#6d4f8d','#cd5e7e', '#e38980','#f7db88'],
    tooltip : {
        show: true,
        formatter: "{b} : {c} ({d}%)"
    },
    legend: {
        data:legendData,
        type: 'scroll',
        orient: 'vertical',
        align: 'left', // 图例标记对其方式
        y: 'center',    //延Y轴居中
        x: 'right', //居右显示
        padding: 10, //调节legend的位置
        formatter:  function(name){
            let total = 0;
            let target;
            for (let i = 0, l = data.length; i < l; i++) {
            total += data[i].value;
            if (data[i].name == name) {
                target = data[i].value;
                }
            }
            return name + ' ' + ((target/total)*100).toFixed(0) + '%';
        }
    },
    toolbox: {
        show : true,
        feature : {
            saveAsImage : {show: true}
        }
    },
    series : getData(data)
};

原文地址 https://www.cnblogs.com/idea360/p/12391416.html

概述

接上一篇 Docker 实战之 MySQL 主从复制, 这里是 Docker 实战系列的第二篇,主要进行 Redis-Cluster 集群环境的快速搭建。Redis 作为基于键值对的 NoSQL 数据库,具有高性能、丰富的数据结构、持久化、高可用、分布式等特性,同时 Redis 本身非常稳定,已经得到业界的广泛认可和使用。

在 Redis 中,集群的解决方案有三种

  1. 主从复制
  2. 哨兵机制
  3. Cluster

Redis Cluster 是 Redis 的分布式解决方案,在 3.0 版本正式推出。

阅读全文 »

使用 fetch

fetch('//xxx.xxx.com/10.pdf', {
    mode: 'cors'
}).then(res => res.blob()).then(blob => {
    const blobURL = window.URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.style.display = 'none'
    link.href = blobURL
    link.setAttribute('download', decodeURI('10.pdf'))
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(blobURL)
})

使用封装好的 $http

$http({
    // 防止出现协议问题,这里不要写具体的 http 或者 https
    url: '//xxx.xxx.com/10.pdf',
    headerType: 'download',
    method: 'get',
    // 不指定默认从 content-disposition 头读取,后端接口也必须要设置 content-disposition 才能读取到
    fileName: 'download.docx'
}).then(res => {

})

window.open

缺点:需要同域、可能会被拦截、浏览器支持预览的文件不会直接下载

window.open('//xxx.xxx.com/10.pdf')

form 表单提交

缺点:浏览器支持预览的文件不会直接下载

export function download (url, params = {}) {
    const form = document.createElement('form')
    form.method = 'post'
    form.action = url
    form.target = '_blank'
    document.body.appendChild(form)
    for (const key in params) {
        const value = params[key]
        if (value) {
            const input = document.createElement('input')
            input.setAttribute('type', 'hidden')
            input.setAttribute('name', key)
            if (isArray(value)) {
                input.setAttribute('value', stringify(value))
            } else {
                input.setAttribute('value', value)
            }
            form.appendChild(input)
        }
    }
    form.submit()
    document.body.removeChild(form)
}

a 标签触发

缺点:可能会被拦截、浏览器支持预览的文件不会直接下载

const link = document.createElement('a')
link.style.display = 'none'
link.href = '//xxx.xxx.com/10.pdf'
link.setAttribute('download', decodeURI('10.pdf'))
document.body.appendChild(link)
link.click()
document.body.removeChild(link)

1 和 2 的方式,无论什么文件都是执行下载

两者都是基于同一个代码包数据库去拉取数据的。

最开始,yarn 是为了弥补 npm 的一些缺陷而出现的,到目前为止,两者的区别越来越小,主要是时间和使用体验上的一些不同了。

1、速度快

npm 第一次安装的对比 54.885s

yarn 第一次安装的对比 64.87s

npm 再次安装时间对比 34.961s

yarn 再次安装时间对比 19.824s

npm 删除包花费的时间对比 43.843s

yarn 删除包花费的时间对比 21.99s

2、npm 安装在国内基本都要设置镜像,而有些 npm 包需要额外的配置才能拉取下来

3、命令使用上,定义在 scripts 里面的命令,npm 需要加上 run,yarn 不需要

npmyarn 常用命令

# 安装依赖
npm i
yarn install

# 启动开发服务
npm run dev
yarn dev

# 打包项目
npm run build
yarn build

# 删除某个依赖
npm uninstall axios
yarn remove axios

# 新增某个依赖
npm install axios --save
yarn add axios

# 新增指定版本的依赖
npm i axios@0.19.2 --save
yarn add axios@0.19.2

多个目录迁移到同一个新的仓库

1、克隆需要迁移的项目代码

git clone git@gitlab.alibaba-inc.com:maru/maru.git

2、同步所有分支信息

git branch -r | grep -v '\->' | while read remote; do git branch --track "${remote#origin/}" "$remote"; done

3、筛选出需要保留的目录

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- src public' --prune-empty -- --all

4、清理 .gitobject

git reset --hard
git for-each-ref --format="%(refname)" refs/original/ |xargs -n 1 git update-ref -d
git reflog expire --expire=now --all
git gc --aggressive --prune=now

5、设置 origin

git remote rm origin
git remote add origin xxx.git

6、推送

git push --all

单个目录迁移到新仓库

只需要将上面的第三步改为下面的这条命令即可:

git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter src -- --all

查找替换

:[range]s/{pattern}/{string}/[flags]

:1,10s/from/to/ 表示在第1到第10行(包含第1,第10行)之间搜索替换
:10s/from/to/ 	表示只在第10行搜索替换
:%s/from/to/ 	表示在所有行中搜索替换
1,$s/from/to/ 	同上

flags 有如下四个选项

  • c confirm,每次替换前询问;
  • e error, 不显示错误;
  • g globle,不询问,整行替换。如果不加 g 选项,则只替换每行的第一个匹配到的字符串;
  • i ignore,忽略大小写
  • 这些选项可以合并使用,如 cgi 表示不区分大小写,整行替换,替换前询问
阅读全文 »