说在前面

   本文难度偏中下,涉及到的点大多为如何在项目中合理应用ts,小部分会涉及一些原理,受众面较广,有无TS基础均可放心食用。
**>>>> 阅完本文,您可能会收获到<<<<**
  1. 若您还不熟悉 TS,那本文可帮助您完成 TS 应用部分的学习,伴随众多 Demo 例来引导业务应用;
  2. 若您比较熟悉 TS,那本文可当作复习文,带您回顾知识,希望能在某些点引发您新发现和思考;
  3. 针对于 class 组件的 IState 和 IProps,类比 Hook 组件的部分写法和思考;

TIPS:超好用的在线 TS 编辑器(诸多配置项可手动配置) 传送门:TS 在线

一、什么是 TS

不扯晦涩的概念,通俗来说 TypeScript 就是 JavaScript 的超集,它具有可选的类型,并可以编译为纯 JavaScript 运行。(笔者一直就把 TypeScript 看作 JavaScript 的 Lint)那么问题来了,为什么 TS 一定要设计成静态的? 或者换句话说,我们为什么需要向 JavaScript 添加类型规范呢 ?

经典自问自答环节——因为它可以解决一些 JS 尚未解决的痛点:

  1. JS 是动态类型的语言,这也意味着在实例化之前我们都不知道变量的类型,但是使用 TS 可以在运行前就避免经典低级错误。 例: Uncaught TypeError:'xxx' is not a function

️ 典中典级别的错误:

JS 就是这样,只有在运行时发生了错误才告诉我有错,但是当 TS 介入后:



好家伙!直接把问题在编辑器阶段抛出,nice!

  1. 懒人狂欢。 规范方便,又不容易出错,对于 VS Code,它能做的最多只是标示出有没有这个属性,但并不能精确的表明这个属性是什么类型,但 TS 可以通过类型推导/反推导(说白话:如果您未明确编写类型,则将使用类型推断来推断您正在使用的类型),从而完美优化了代码补全这一项:

第一个 Q&A——思考 :

那么我们还能想到在业务开发中 TS 解决了哪些 JS 的痛点呢?(提问)

回答,总结,补充:

-对函数参数的类型限制;

-对数组和对象的类型限制,避免定义出错 例如数据解构复杂或较多时,

可能会出现数组定义错误 a = { }, if (a.length){ // xxxxx }

-let functionA = 'jiawen' // 实际上 let functionA: string = 'jiawen'​

  1. 使我们的应用代码更易阅读和维护,如果定义完善,可以通过类型大致明白参数的作用;

相信通过上述简单的bug-demo,各位已对TS有了一个初步的重新认识

接下来的章节便正式介绍我们在业务开发过程中如何用好TS

二、怎么用 TS

 在业务中如何用TS/如何用好TS?这个问题其实和 " 在业务中怎么用好一个API " 是一样的。首先要知道这个东西在干嘛,参数是什么,规则是什么,能够接受有哪些扩展......等等。 简而言之,撸它!

TS 常用类型归纳

通过对业务中常见的 TS 错误做出的一个综合性总结归纳,希望 Demos 会对您有收获

元语(primitives)之 string number boolean

  笔者把基本类型拆开的原因是: 不管是中文还是英文文档,primitives/元语/元组 这几个名词都频繁出镜,笔者理解的白话:希望在类型约束定义时,使用的是字面量而不是内置对象类型,官方文档:

let a: string = 'jiawen';
let flag: boolean = false;
let num: number = 150 interface IState: {
flag: boolean;
name: string;
num: number;
}

元组

// 元组类型表示已知元素数量和类型的数组,各元素的类型不必相同,但是对应位置的类型需要相同。

let x: [string, number];
x = ['jiawen', 18]; // ok
x = [18, 'jiawen']; // Erro
console.log(x[0]); // jiawen

undefined null

let special: string = undefined
// 值得一提的是 undefined/null 是所有基本类型的子类,
// 所以它们可以任意赋值给其他已定义的类型,这也是为什么上述代码不报错的原因

object 和 { }

// object 表示的是常规的 Javascript对象类型,非基础数据类型
const offDuty = (value: object) => {
console.log("value is ", value);
} offDuty({ prop: 0}) // ok
offDuty(null) offDuty(undefined) // Error
offDuty(18) offDuty('offDuty') offDuty(false) // Error // {} 表示的是 非null / 非undefined 的任意类型
const offDuty = (value: {}) => {
console.log("value is ", value);
} offDuty({ prop: 0}) // ok
offDuty(null) offDuty(undefined) // Error
offDuty(18) offDuty('offDuty') offDuty(false) // ok
offDuty({ toString(){ return 333 } }) // ok // {} 和Object几乎一致,区别是Object会对Object内置的 toString/hasOwnPreperty 进行校验
const offDuty = (value: Object) => {
console.log("value is ", value);
} offDuty({ prop: 0}) // ok
offDuty(null) offDuty(undefined) // Error
offDuty(18) offDuty('offDuty') offDuty(false) // ok
offDuty({ toString(){ return 333 } }) // Error 如果需要一个对象类型,但对属性没有要求,建议使用 object
{} 和 Object 表示的范围太大,建议尽量不要使用

object of params

// 我们通常在业务中可多采用点状对象函数(规定参数对象类型)

const offDuty = (value: { x: number; y: string }) => {
console.log("x is ", value.x);
console.log("y is ", value.y);
} // 业务中一定会涉及到"可选属性";先简单介绍下方便快捷的“可选属性” const offDuty = (value: { x: number; y?: string }) => {
console.log("必选属性x ", value.x);
console.log("可选属性y ", value.y);
console.log("可选属性y的方法 ", value.y.toLocaleLowerCase());
}
offDuty({ x: 123, y: 'jiawen' })
offDuty({ x: 123 }) // 提问: 上述代码有问题吗? 答案: // offDuty({ x: 123 }) 会导致结果报错value.y.toLocaleLowerCase()
// Cannot read property 'toLocaleLowerCase' of undefined 方案1: 手动类型检查
const offDuty = (value: { x: number; y?: string }) => {
if (value.y !== undefined) {
console.log("可能不存在的 ", value.y.toUpperCase());
}
}
方案2:使用可选属性 (推荐)
const offDuty = (value: { x: number; y?: string }) => {
console.log("可能不存在的 ", value.y?.toLocaleLowerCase());
}

unknown 与 any

// unknown 可以表示任意类型,但它同时也告诉TS, 开发者对类型也是无法确定,做任何操作时需要慎重

let Jiaven: unknown

Jiaven.toFixed(1) // Error

if (typeof Jiaven=== 'number') {
Jiaven.toFixed(1) // OK
} 当我们使用any类型的时候,any会逃离类型检查,并且any类型的变量可以执行任意操作,编译时不会报错 anyscript === javascript 注意:any 会增加了运行时出错的风险,不到万不得已不要使用; 如果遇到想要表示【不知道什么类型】的场景,推荐优先考虑 unknown

union 联合类型

union也叫联合类型,由两个或多个其他类型组成,表示可能为任何一个的值,类型之间用 ' | '隔开

type dayOff = string | number | boolean

联合类型的隐式推导可能会导致错误,遇到相关问题请参考语雀 code and tips —— 《TS的隐式推导》

.值得注意的是,如果访问不共有的属性的时候,会报错,访问共有属性时不会.上个最直观的demo

function dayOff (value: string | number): number {
return value.length;
}
// number并不具备length,会报错,解决方法:typeof value === 'string' function dayOff (value: string | number): number {
return value.toString();
}
// number和string都具备toString(),不会报错

never

// never是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。

// 那never在实际开发中到底有什么作用? 这里笔者原汁原味照搬尤雨溪的经典解释来做第一个例子

第一个例子,当你有一个 union type:

interface Foo {
type: 'foo'
} interface Bar {
type: 'bar'
} type All = Foo | Bar 在 switch 当中判断 type,TS是可以收窄类型的 (discriminated union): function handleValue(val: All) {
switch (val.type) {
case 'foo':
// 这里 val 被收窄为 Foo
break
case 'bar':
// val 在这里是 Bar
break
default:
// val 在这里是 never
const exhaustiveCheck: never = val
break
}
} 注意在 default 里面我们把被收窄为 never 的 val 赋值给一个显式声明为 never 的变量。 如果一切逻辑正确,那么这里应该能够编译通过。但是假如后来有一天你的同事改了 All 的类型: type All = Foo | Bar | Baz 然而他忘记了在 handleValue 里面加上针对 Baz 的处理逻辑,
这个时候在 default branch 里面 val 会被收窄为 Baz,导致无法赋值给 never,产生一个编译错误。
所以通过这个办法,你可以确保 handleValue 总是穷尽 (exhaust) 了所有 All 的可能类型。
第二个用法  返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
throw new Error(message);
} 第三个用法 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
while (true) {}
}

void

interface IProps {
onOK: () => void
}
void 和 undefined 功能高度类似,但void表示对函数的返回值并不在意或该方法并无返回值

enum

笔者认为ts中的enum是一个很有趣的枚举类型,它的底层就是number的实现

1.普通枚举
enum Color {
Red,
Green,
Blue
};
let c: Color = Color.Blue;
console.log(c); // 2 2.字符串枚举
enum Color {
Red = 'red',
Green = 'not red',
}; 3.异构枚举 / 有时也叫混合枚举
enum Color {
Red = 'red',
Num = 2,
};
<第一个坑>

enum Color {
A, // 0
B, // 1
C = 20, // 20
D, // 21
E = 100, // 100
F, // 101
} 若初始化有部分赋值,那么后续成员的值为上一个成员的值加1
<第二个坑> 这个坑是第一个坑的延展,稍不仔细就会上当!

const getValue = () => {
return 23
} enum List {
A = getValue(),
B = 24, // 此处必须要初始化值,不然编译不通过
C
}
console.log(List.A) // 23
console.log(List.B) // 24
console.log(List.C) // 25 如果某个属性的值是计算出来的,那么它后面一位的成员必须要初始化值。
否则将会 Enum member must have initializer.

泛型

笔者理解的泛型很白话:先不指定具体类型,通过传入的参数类型来得到具体类型

我们从下述的 filter-demo 入手,探索一下为什么一定需要泛型

  • 泛型的基础样式
function fun<T>(args: T): T {
return args
}

如果没接触过,是不是会觉得有点懵? 没关系!我们直接从业务角度深入——

1.刚开始的需求:过滤数字类型的数组

declare function filter(
array: number[],
fn: (item: unknown) => boolean
) : number[]; 2.产品改了需求:还要过滤一些字符串 string[] 彳亍,那就利用函数的重载, 加一个声明, 虽然笨了点,但是很好理解 declare function filter(
array: string[],
fn: (item: unknown) => boolean
): string[]; declare function filter(
array: number[],
fn: (item: unknown) => boolean
): number[]; 3.产品又来了! 这次还要过滤 boolean[]、object[] .......... 这个时候如果还是选择重载,将会大大提升工作量,代码也会变得越来越累赘,这个时候泛型就出场了,
它从实现上来说更像是一种方法,通过你的传参来定义类型,改造如下: declare function filter<T>(
array: T[],
fn: (item: unknown) => boolean
): T[];

泛型中的可以是任意,但是大部分偏好为 T、U、S 等,

当我们把泛型理解为一种方法实现后,那么我们便很自然的联想到:方法有多个参数、默认值,泛型也可以

type Foo<T, U = string> = { // 多参数、默认值
foo: Array<T> // 可以传递
bar: U
} type A = Foo<number> // type A = { foo: number[]; bar: string; }
type B = Foo<number, number> // type B = { foo: number[]; bar: number; }

既然是“函数”,那也会有“限制”,下文列举一些稍微常见的约束

1. extends: 限制 T 必须至少是一个 XXX 的类型

type dayOff<T extends HTMLElement = HTMLElement> = {
where: T,
name: string
}
2. Readonly<T>: 构造一个所有属性为readonly,这意味着无法重新分配所构造类型的属性。

interface Eat {
food: string;
} const todo: Readonly<Eat> = {
food: "meat beef milk",
}; todo.food = "no food"; // Cannot assign to 'title' because it is a read-only property.
3. Pick<T,K>: 从T中挑选出一些K属性

interface Todo {
name: string;
job: string;
work: boolean; type TodoPreview = Pick<Todo, "name" | "work">; const todo: TodoPreview = {
name: "jiawen",
work: true,
};
todo;
4. Omit<T, K>: 结合了 T 和 K 并忽略对象类型中 K 来构造类型。

interface Todo {
name: string;
job: string;
work: boolean;
} type TodoPreview = Omit<Todo, "work">; const todo: TodoPreview = {
name: "jiawen",
job: 'job',
};
5.Record: 约束 定义键类型为 Keys、值类型为 Values 的对象类型。

enum Num {
A = 10001,
B = 10002,
C = 10003
} const NumMap: Record<Num, string> = {
[Num.A]: 'this is A',
[Num.B]: 'this is B'
}
// 类型 "{ 10001: string; 10002: string; }" 中缺少属性 "10003",
// 但类型 "Record<ErrorCodes, string>" 中需要该属性,所以我们还可以通过Record来做全面性检查 keyof 关键字可以用来获取一个对象类型的所有 key 类型
type User = {
id: string;
name: string;
}; type UserKeys = keyof User; // "id" | "name" 改造如下 type Record<K extends keyof any, T> = {
[P in K]: T;
};
此时的 T 为 any;
还有一些不常用,但是很易懂的:

6. Extract<T, U>  从T,U中提取相同的类型

7. Partial<T>    所有属性可选

type User = {
id?: string,
gender: 'male' | 'female'
} type PartialUser = Partial<User> // { id?: string, gender?: 'male' | 'female'} type Partial<T> = { [U in keyof T]?: T[U] } 8. Required<T> 所有属性必须 << === >> 与Partial相反 type User = {
id?: string,
sex: 'male' | 'female'
} type RequiredUser = Required<User> // { readonly id: string, readonly gender: 'male' | 'female'} function showUserProfile (user: RequiredUser) {
console.log(user.id) // 这时候就不需要再加?了
console.log(user.sex)
}
type Required<T> = { [U in keyof T]-?: T[U] }; -? : 代表去掉?

三、TS 的一些须知

TS 的 type 和 interface

  • interface(接口) 只能声明对象类型,支持声明合并(可扩展)。
interface User {
id: string
} interface User {
name: string
} const user = {} as User console.log(user.id);
console.log(user.name);
  • type(类型别名)不支持声明合并 -- l 类型
type User = {
id: string,
} if (true) {
type User = {
name: string,
} const user = {} as User;
console.log(user.name);
console.log(user.id) // 类型“User”上不存在属性“id”。
}



type 和 interface 异同点总结:

  1. 通常来讲 type 更为通用,右侧可以是任意类型,包括表达式运算,以及映射等;
  2. 凡是可用 interface 来定义的,type 也可;
  3. 扩展方式也不同,interface 可以用 extends 关键字进行扩展,或用来 implements 实现某个接口;
  4. 都可以用来描述一个对象或者函数;
  5. type 可以声明基本类型别名、联合类型、元组类型,interface 不行;
  6. ️ 但如果你是在开发一个包,模块,允许别人进行扩展就用 interface,如果需要定义基础数据类型或者需要类型运算,使用 type。
  7. interface 可以被多次定义,并会被视作合并声明,而 type 不支持;
  8. 导出方式不同,interface 支持同时声明并默认导出,而 typetype 必须先声明后导出;

TS 的脚本模式和模块模式

Typescript 存在两种模式,区分的逻辑是,文件内容包不包含 import 或者 export 关键字

脚本模式(Script) 一个文件对应一个 html 的 script 标签,

模块模式(Module)一个文件对应一个 Typescript 的模块。

脚本模式下,所有变量定义,类型声明都是全局的,多个文件定义同一个变量会报错,同名 interface 会进行合并;而模块模式下,所有变量定义,类型声明都是模块内有效的。

两种模式在编写类型声明时也有区别,例如脚本模式下直接 declare var GlobalStore 即可为全局对象编写声明。

例子:

  • 脚本模式下直接 declare var GlobalStore 即可为全局对象编写声明。
GlobalStore.foo = "foo";
GlobalStore.bar = "bar"; // Error declare var GlobalStore: {
foo: string;
};
  • 模块模式下,要为全局对象编写声明需要 declare global
GlobalStore.foo = "foo";
GlobalStore.bar = "bar"; declare global {
var GlobalStore: {
foo: string;
bar: string;
};
} export {}; // export 关键字改变文件的模式

TS 的索引签名

  • 索引签名可以用来定义对象内的属性、值的类型,例如定义一个 React 组件,允许 Props 可以传任意 key 为 string,value 为 number 的 props
interface Props {
[key: string]: number
} <Component count={1} /> // OK
<Component count={true} /> // Error
<Component count={'1'} /> // Error

TS 的类型键入

  • Typescript 允许像对象取属性值一样使用类型
type User = {
userId: string
friendList: {
fristName: string
lastName: string
}[]
} type UserIdType = User['userId'] // string
type FriendList = User['friendList'] // { fristName: string; lastName: string; }[]
type Friend = FriendList[number] // { fristName: string; lastName: string; }
  • 在上面的例子中,我们利用类型键入的功能从 User 类型中计算出了其他的几种类型。FriendList[number]这里的 number 是关键字,用来取数组子项的类型。在元组中也可以使用字面量数字得到数组元素的类型。
type group = [number, string]
type First = group[0] // number
type Second = group[1] // string

TS 的断言

  • 类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的
function getLength(value: string | number): number {
if (value.length) {
return value.length;
} else {
return value.toString().length;
} // 这个问题在object of parmas已经提及,不再赘述 修改后: if ((<string>value).length) {
return (<string>value).length;
} else {
return something.toString().length;
}
}
断言的两种写法

1. <类型>值:  <string>value

2. 或者 value as string

特别注意!!! 断言成一个联合类型中不存在的类型是不允许的

function toBoolean(something: string | number): boolean {
return <boolean>something;
}
  • 非空断言符 !

TypeScript 还具有一种特殊的语法,用于从类型中删除 null 和 undefined 不进行任何显式检查。!在任何表达式之后写入实际上是一个类型断言,表明该值不是 null 或 undefined

function liveDangerously(x?: number | undefined | null) {
// 推荐写法
console.log(x!.toFixed());
}

四、如何在 Hook 组件中使用 TS

usestate

  • useState 如果初始值不是 null/undefined 的话,是具备类型推导能力的,根据传入的初始值推断出类型;初始值是 null/undefined 的话则需要传递类型定义才能进行约束。一般情况下,还是推荐传入类型(通过 useState 的第一个泛型参数)。
// 这里ts可以推断 value的类型并且能对setValue函数调用进行约束
const [value, setValue] = useState(0); interface MyObject {
name: string;
age?: number;
} // 这里需要传递MyObject才能约束 value, setValue
// 所以我们一般情况下推荐传入类型
const [value, setValue] = useState<MyObject>(null);

-----as unkonwn as unkownun

useEffect useLayoutEffect

  • 没有返回值,无需类型传递和约束

useMemo useCallback

  • useMemo 无需传递类型, 根据函数的返回值就能推断出类型。
  • useCallback 无需传递类型,根据函数的返回值就能推断出类型。

但是注意函数的入参需要定义类型,不然将会推断为 any!

const value = 10;

const result = useMemo(() => value * 2, [value]); // 推断出result是number类型

const multiplier = 2;
// 推断出 (value: number) => number
// 注意函数入参value需要定义类型
const multiply = useCallback((value: number) => value * multiplier, [multiplier]);

useRef

  • useRef 传非空初始值的时候可以推断类型,同样也可以通过传入第一个泛型参数来定义类型,约束 ref.current 的类型。
1. 如果传值为null
const MyInput = () => {
const inputRef = useRef<HTMLInputElement>(null); // 这里约束inputRef是一个html元素
return <input ref={inputRef} />
} 2. 如果不为null
const myNumberRef = useRef(0); // 自动推断出 myNumberRef.current 是number类型
myNumberRef.current += 1;

useContext

  • useContext 一般根据传入的 Context 的值就可以推断出返回值。一般无需显示传递类型
type Theme = 'light' | 'dark';
// 我们在createContext就传了类型了
const ThemeContext = createContext<Theme>('dark'); const App = () => (
<ThemeContext.Provider value="dark">
<MyComponent />
</ThemeContext.Provider>
) const MyComponent = () => {
// useContext根据ThemeContext推断出类型,这里不需要显示传
const theme = useContext(ThemeContext);
return <div>The theme is {theme}</div>;

五、关于 TS 的一些思考

1. 关于 TSC 如何把 TS 代码转换为 JS 代码

这个部分比较冗长,后续可以单独出一篇文章(2)来专门探索。
  • 不过,tsconfig.json 的部分常用的配置属性表还是值得一提的
{
"compilerOptions": {
"noEmit": true, // 不输出文件
"allowUnreachableCode": true, // 不报告执行不到的代码错误。
"allowUnusedLabels": false, // 不报告未使用的标签错误
"alwaysStrict": false, // 以严格模式解析并为每个源文件生成 "use strict"语句
"baseUrl": ".", // 工作根目录
"lib": [ // 编译过程中需要引入的库文件的列表
"es5",
"es2015",
"es2016",
"es2017",
"es2018",
"dom"
]
"experimentalDecorators": true, // 启用实验性的ES装饰器
"jsx": "react", // 在 .tsx文件里支持JSX
"sourceMap": true, // 是否生成map文件
"module": "commonjs", // 指定生成哪个模块系统代码
"noImplicitAny": false, // 是否默认禁用 any
"removeComments": true, // 是否移除注释
"types": [ //指定引入的类型声明文件,默认是自动引入所有声明文件,一旦指定该选项,则会禁用自动引入,改为只引入指定的类型声明文件,如果指定空数组[]则不引用任何文件
"node", // 引入 node 的类型声明
],
"paths": { // 指定模块的路径,和baseUrl有关联,和webpack中resolve.alias配置一样
"src": [ //指定后可以在文件之直接 import * from 'src';
"./src"
],
},
"target": "ESNext", // 编译的目标是什么版本的
"outDir": "./dist", // 输出目录
"declaration": true, // 是否自动创建类型声明文件
"declarationDir": "./lib", // 类型声明文件的输出目录
"allowJs": true, // 允许编译javascript文件。
},
// 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
"include": [
"src/**/*"
],
// 指定一个排除列表(include的反向操作)
"exclude": [
"demo.ts"
],
// 指定哪些文件使用该配置(属于手动一个个指定文件)
"files": [
"demo.ts"
]
}

2. TS 泛型的底层实现

关于TS泛型进阶篇 链接:[https://dtstack.yuque.com/rd-center/sm6war/wae3kg](https://dtstack.yuque.com/rd-center/sm6war/wae3kg)

这个部分比较复杂,笔者还需沉淀,欢迎各位直接留言或在文章中补充!!!

3. TS 泛型+类型反推在实际开发中的应用

TS基础应用 & Hook中的TS的更多相关文章

  1. 如何在vue中使用ts

    注意:此文并不是把vue改为全部替换为ts,而是可以在原来的项目中植入ts文件,目前只是实践阶段,向ts转化过程中的过渡. ts有什么用? 类型检查.直接编译到原生js.引入新的语法糖 为什么用ts? ...

  2. (原)关于MEPG-2中的TS流数据格式学习

    关于MEPG-2中的TS流数据格式学习 Author:lihaiping1603 原创:http://www.cnblogs.com/lihaiping/p/8572997.html 本文主要记录了, ...

  3. vue项目中使用ts(typescript)入门教程

    最近项目需要将原vue项目结合ts的使用进行改造,这个后面应该是中大型项目的发展趋势,看到一篇不错的入门教程,结合它并进行了一点拓展记录之.本文从安装到vue组件编写进行了说明,适合入门. 1.引入T ...

  4. iOS开发OC基础:Xcode中常见英文总结,OC常见英文错误

    在开发的过程中难免会遇到很多的错误,可是当看到系统给出的英文时,又不知道是什么意思.所以这篇文章总结了Xcode中常见的一些英文单词及词组,可以帮助初学的人快速了解给出的提示.多练习,就肯定能基本掌握 ...

  5. 完全零基础在Linux中安装 JDK

    完全零基础在Linux中安装 JDK 总体思路:先确定没有Java程序了 — 然后创建相应路径文件夹 — 下载JDK — 解压到当前路径 — 自定义文件名称 — 配置环境变量 — 检查是否安装成功 第 ...

  6. TS基础笔记

    TS优势 更好的错误的提示,开发中及时发现问题:编辑器语法提示更完善:类型声明可以看出数据结构的语义,可读性更好; TS环境搭建 1.安装node;2.npm install typescript@3 ...

  7. TYPESCRIPT中文教程基础部分下----翻译自TS官方

    type 别名 我们已经使用过 object 和 联合的方式 直接声明类型.但是某个类型在使用多次的情况下就要用到别名了. 别名的语法就像是在定义一个具名的对象一样: type Point = { x ...

  8. TypeScript中文教程基础部分上----翻译自TS官方

    为什么使用TS? js中每一个值在不同的操作运行中表现出一系列不同的行为,比如说下面这个例子: message.toLowerCase();message(); 逐行看下,第一行调用了message的 ...

  9. FFMPEG中关于ts流的时长估计的实现(转)

    最近在做H.265 编码,原本只是做编码器的实现,但客户项目涉及到ts的封装,搞得我不得不配合了解点ts方面的东西.下面技术文档不错,转一下. ts流中的时间估计 我们知道ts流中是没有时间信息的,我 ...

随机推荐

  1. JSX语法详解

    一.基础1.JSX是什么JSX是一种像下面这样的语法: const element = <h1>Hello, world!</h1>;1它是一种JavaScript语法扩展,在 ...

  2. istio部署问题Q&A

    端口绑定无权限 创建Gateway,提示绑定端口无权限. 2020-12-27T12:25:30.974288Z warning envoy config gRPC config for type.g ...

  3. kubernetes客户端client-go使用

    下载地址: https://github.com/kubernetes/client-go 官方使用文档参考:https://v1-16.docs.kubernetes.io/docs/referen ...

  4. S3待机 S4休眠

    https://hceng.cn/2018/01/18/Linux%E7%94%B5%E6%BA%90%E7%AE%A1%E7%90%86/ 1.1系统睡眠模型Suspend On (on) S0 - ...

  5. nvm、nrm、npm 安装和使用详解

    一.nvm的安装和使用   nvm全称Node Version Manager是 Nodejs 版本管理器,它让我们能方便的对 Nodejs 的版 本进行切换. nvm 的官方版本只支持 Linux ...

  6. Java和JDK版本的关系-(转载)

    JAVA的版本最开始是1995年的JDK Alpha and Beta版本,第二年发布JDK1.0版本之后就是JDK1.1,JDK1.2.到1998年,不再叫JDK了,而是叫J2SE,但是版本号还是继 ...

  7. 1.4linux文件和目录常用命令

    文件和目录常用命令 目标 查看目录内容 ls 切换目录 cd 创建和删除操作 touch rm mkdir 拷贝和移动文件 cp mv 查看文件内容 cat more grep 其他 echo 重定向 ...

  8. python内存管理总结

    之前在学习与工作中或多或少都遇到关于python内存管理的问题,现在将其梳理一下. python内存管理机制 第0层 操作系统提供的内存管理接口 c实现 第1层 基于第0层操作系统内存管理接口包装而成 ...

  9. jrebel 插件使用

    最近遇到一件神奇的事情,idea原本配置了热部署,但是修改java文件之后需要重启俩次才能编译成功,网上各种问度娘都没有解决,偶尔看到了jrebel这个插件,折腾了一番,终于完美解决,记录一下,供后来 ...

  10. Django 基本操作

    www.djangobook.comdjangobook.py3k.cn 1.django-admin.py startproject mysite2.manage.py startapp blog3 ...