共计 16963 个字符,预计需要花费 43 分钟才能阅读完成。
原文链接:TypeScript 入门
之前浏览 vue 源码的时候发现有 TypeScript,一脸懵逼,因而须要入个门。
最近在新环境的日常工作中也须要用到 TypeScript,学习过程中遇到一些纳闷,做了记录。
集体感觉还是比拟适宜 TypeScript 入门 的同学浏览的,因为我遇到的这些纳闷,可能你也会遇到。
- ts 类型中的?,<> 意思是什么?
- 什么是 duck typing?
- constructor 之前的变量定义是什么?
- declare 是什么?
- ts 中 unknown, void, null 和 undefined,never 区别是什么?
- ts 中的泛型束缚是什么?
- 数组类型的两种定义形式
- ts 中的类型断言
- 泛型函数与泛型接口
- 如何了解 as const?
- declare global 是什么意思?
- 如何在 TypeScript 环境减少一个全局变量?
- interface 能够继承吗?
- typescript 中的 & 是什么意思?
- interface 与 type 的区别是什么?
- enum 作为一种类型是什么意思?
- 我的项目中 xxx.d.ts 的 declare module ‘*.scss’ 是什么意思?
declare module
还能够做什么? - typescript 如何束缚 Promise 的类型?
- typescript 中的 keyof 如何应用?
- typescript 中的 typeof 如何应用?
- typescript 中的
non-null operator
是什么?
ts 类型中的? 意思是什么?
// https://github.com/vuejs/vue/blob/dev/src/core/observer/watcher.js | |
before: ?Function; | |
options?: ?Object, |
这是 ts 的 interface 中的一个概念。ts 的 interface 就是 ”duck typing” 或者 ”structural subtyping”,类型查看次要关注 the shape that values have。因而咱们先来相熟一下 interface,再引出? 的解释。
TypeScript 一般形式定义函数:
function print(obj: {label: string}) {console.log(obj.label); | |
} | |
let foo = {size: 10, label: "这是 foo, 10 斤"}; | |
print(foo); |
TypeScript interface 形式定义函数:
interface labelInterface {label: string;} | |
function print(obj: labelInterface) {console.log(obj.label); | |
} | |
let foo = {size: 10, label: "这是 foo, 10 斤"}; | |
print(foo); |
进入正题,TypeScript 中的 ?
是什么意思?Optional Properties。
Optional Properties
- 并不是 interface 中的所有属性都是 required 的,一些存在特定条件下,一些基本不存在。
- Optional Properties 实用于 ”option bags” 的设计模式,这种设计模式意思是:咱们传递一个对象到函数,这个函数只有几个属性,没有其余更多的属性。
-
Optional Property 的益处在于,清晰的看清楚有哪些属性,避免传入不属于该 interface 的属性。
interface SquareConfig { color?: string; width?: number; } 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.color; } if (config.width) {newSquare.area = config.width * config.width;} return newSquare; } let mySquare = createSquare({color: "black"}); Interfaces with optional properties are written similar to other interfaces, with each optional property denoted by a ? at the end of the property name in the declaration.
什么是 ?
和 Optional Properties 呢?interface 的某些非 required 属性名的开端,增加 ?
这是一个 optional property,其实就是字面意思,条件属性。
Optional Property 只是属性名,也就是 options?: ?Object,
中 options 后的问号,拿属性值类型前的问号是什么意思,也就是 ?Object
,是什么意思?
此处的问号代表属性值类型是否能够是 null 类型,然而只有 strictNullChecks 为 on 时,值类型能力为 null。
/** | |
* @type {?number} | |
* strictNullChecks: true -- number | null | |
* strictNullChecks: off -- number | |
* */ | |
var nullable; |
咱们的例子中,options?:?Object
的意思是 options 的值类型能够是 Object,null(仅在 strictNullChecks 为 true 时容许)。
ts 类型中的 <>
什么意思?
deps: Array<Dep>a | |
newDeps: Array<Dep> |
ts 中的数组类型与 java 中的定义相似:
let list: number[] = [1, 2, 3]; | |
let list: Array<number> = [1, 2, 3]; |
什么是 duck typing?
duck test。如果 ” 走路像鸭子,叫声像鸭子,那么这就是鸭子 ”。
在 computer programming,用于 ’ 判断对象是否能够依照预期的目标应用 ’。
通常的 typing 中,适用性取决于对象的 type。duck typing 不一样,对象的适用性取决于 指定 method 或 property 的存在与否,而不是取决于对象本身的类型。
前端工程师根本都是 duck typing,因为 JavaScript 没有 type。 – 这话是我说的
Python3 example
class Duck: | |
def fly(self): | |
print("Duck flying") | |
class Airplane: | |
def fly(self): | |
print("Airplane flying") | |
class Whale: | |
def swim(self): | |
print("Whale swimming") | |
def lift_off(entity): | |
entity.fly() | |
duck = Duck() | |
airplane = Airplane() | |
whale = Whale() | |
lift_off(duck) # prints `Duck flying` | |
lift_off(airplane) # prints `Airplane flying` | |
lift_off(whale) # Throws the error `'Whale' object has no attribute 'fly'` |
Javascript example
class Duck {fly() {console.log("Duck flying") | |
} | |
} | |
class Airplane {fly() {console.log("Airplane flying") | |
} | |
} | |
class Whale {swim() {console.log("Whale swimming") | |
} | |
} | |
function liftOff(entity) {entity.fly() | |
} | |
const duck = new Duck(); | |
const airplane = new Airplane(); | |
const whale = new Whale(); | |
liftOff(duck); // Duck flying | |
liftOff(airplane); // Airplane flying | |
liftOff(whale); // Uncaught TypeError: entity.fly is not a function |
constructor 之前的变量定义是什么?
例如 vnode 的定义:
export default class VNode { | |
tag: string | void; | |
data: VNodeData | void; | |
children: ?Array<VNode>; | |
text: string | void; | |
elm: Node | void; | |
ns: string | void; | |
context: Component | void; // rendered in this component's scope | |
key: string | number | void; | |
componentOptions: VNodeComponentOptions | void; | |
componentInstance: Component | void; // component instance | |
parent: VNode | void; // component placeholder node | |
// strictly internal | |
raw: boolean; // contains raw HTML? (server only) | |
isStatic: boolean; // hoisted static node | |
isRootInsert: boolean; // necessary for enter transition check | |
isComment: boolean; // empty comment placeholder? | |
isCloned: boolean; // is a cloned node? | |
isOnce: boolean; // is a v-once node? | |
asyncFactory: Function | void; // async component factory function | |
asyncMeta: Object | void; | |
isAsyncPlaceholder: boolean; | |
ssrContext: Object | void; | |
fnContext: Component | void; // real context vm for functional nodes | |
fnOptions: ?ComponentOptions; // for SSR caching | |
fnScopeId: ?string; // functional scope id support | |
constructor () | |
... | |
} |
http://www.typescriptlang.org…
typeScript 中的 class 要比 es6 的多一项:property。这和 java 或者 c# 中的统一。
property | |
constructor | |
method |
实际上 es6 提供了一种公有变量,仅仅能在 class 外部拜访。
class Rectangle { | |
#height = 0; | |
#width; | |
constructor(height, width) { | |
this.#height = height; | |
this.#width = width; | |
} | |
} |
冒号前面的:VNode 什么意思?
export function cloneVNode (vnode: VNode): VNode {...}
TypeScript 中的函数返回值类型。
declare 是什么?
申明这是一个 definition。
- declare 是 ts 中用于 写定义文件 的关键字。
- declare 能够定义全局变量,全局函数,全局命名空间,class 等等。
-
declare 能够依照上面这样去应用:
declare var foo:number; declare function greet(greeting: string): void; declare namespace myLib {function makeGreeting(s: string): string; let numberOfGreeting: number; } declare function getWidget(n: number): Widget; declare function getWidget(s: string): Widget[]; declare class Greeter {constructor(greeting: string); greeting: string; showGreeting(): void;}
ts 中 any,unknown, void, null 和 undefined,never 区别是什么?
null,undefined 就是 js 中的意思。
any: 任意类型,审慎应用,防止使 typescript 变成 anyscript
unknown: 与 any 相似,然而比 any 更加平安
void: 通常用于返回值的函数
never:never occur 从来不会产生的类型,例如永远不会有后果的,抛出异样或者死循环。
ts 中的泛型束缚是什么?
基于 string(boolean, Function)类型
function loggingIdentity<T extends string>(arg: T): T {console.log(arg.length); | |
return arg; | |
} | |
loggingIdentity("hello"); // 5 | |
loggingIdentity(2); // Argument of type 'number' is not assignable to parameter of type 'string'. |
基于自定义的 interface
interface Lengthwise {length: number;} | |
function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length); // Now we know it has a .length property, so no more error | |
return arg; | |
} | |
loggingIdentity(3); // Error, number doesn't have a .length property | |
loggingIdentity({length: 10, value: 3}); // 10 |
ts2.8 公布阐明
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html | |
type TypeName<T> = T extends string | |
? "string" | |
: T extends number | |
? "number" | |
: T extends boolean | |
? "boolean" | |
: T extends undefined | |
? "undefined" | |
: T extends Function | |
? "function" | |
: "object"; | |
type T0 = TypeName<string>; // "string" | |
type T1 = TypeName<"a">; // "string" | |
type T2 = TypeName<true>; // "boolean" | |
type T3 = TypeName<() => void>; // "function" | |
type T4 = TypeName<string[]>; // "object" |
同时反对 type 和 interface 两种类型的泛型束缚
interface reduxModel<T> {reducers: T extends string ? {[x in T]: () => void}: T, | |
} | |
type TType = "foo" | "bar" | 'baz' | |
interface TInterface {"foo": () => void, | |
"bar": () => void, | |
'baz': () => void} | |
const ireducers = {"foo": () => void | |
} | |
const model : reduxModel<TType> = { | |
reducers: ireducers | |
// 失常运行 | |
} | |
const model : reduxModel<TInterface> = { | |
reducers: ireducers | |
// Type '{foo: () => undefined; }' is missing the following properties from type 'TInterface': "bar", 'baz' | |
} |
数组类型的两种定义形式
Array< 类型 >
Array 前面加一个 <>,<> 内申明元素类型。
type Foo= Array<string>;
interface Bar { | |
baz: Array<{ | |
name: string, | |
age: number, | |
}> | |
} |
类型[]
元素类型前面加一个[]。
type Foo = string[]
interface Bar { | |
baz : { | |
name: string, | |
age: number, | |
}[]} |
ts 中的类型断言
TypeScript 容许咱们笼罩推断和剖析出的视图类型为咱们想要的任意形式,这种机制叫做类型断言(Type Assertion),类型断言会通知编译器你比它更加晓得具体是哪种类型,编译器不必再二次推断了。
类型断言往往是产生在编译器编译期间,用于提醒编译器如何剖析咱们的代码。
- 语法
- 迁徙 js 代码
- 类型断言的问题
- 指定 event 类型
- 慎用 as any 和 as unknown
- type 与类型断言
语法
interface Foo {name: string,} | |
type Any = any; | |
let a:Foo = {} as Foo; | |
let a:Foo = {} as Any; |
any 是任意类型的子类型,所以任意类型都能够被 as any,还是倡议审慎应用,防止变为 anyscript。
迁徙 js 代码
var foo = {}; | |
foo.bar = 123; // Error: property 'bar' does not exist on `{}` | |
foo.bas = 'hello'; // Error: property 'bas' does not exist on `{}` |
interface Foo { | |
bar: number; | |
bas: string; | |
} | |
var foo = {} as Foo; | |
foo.bar = 123; | |
foo.bas = 'hello'; // 正文掉这一行也不会报错 |
类型断言的问题
foo.bas = ‘hello’; // 正文掉这一行也不会报错
如果是上面的形式就会报错了,会提醒短少 bas 的定义
interface Foo { | |
bar: number; | |
bas: string; | |
} | |
var foo : Foo= {bar: 123}; |
所以说,类型断言是不够谨严的,倡议应用 var foo : Foo
这种形式。
指定 event 类型
function handler (event: Event) {let mouseEvent = event as MouseEvent;}
function handler(event: Event) {let element = event as HTMLElement; // HTMLElement 不是一个齐全的 event 子类型,因而不能充沛重叠,须要加一个 unknown 或者 any}
二次断言编译提醒勾销:
function handler(event: Event) {let element = event as unknown as HTMLElement; // Okay!}
慎用 as any 和 as unknown
通常状况是类型断言 S 和 T 的话,S 为 T 的子类型,或者 T 为 S 的子类型,这种是绝对平安的。
如果是用 as any 或者 as unknown,是十分不平安的。慎用!慎用!
// 审慎应用 | |
as any | |
as known |
type 与类型断言
type keys = 'foo' | 'bar' | 'baz'
,obj[key as keys]
是什么意思?
与 variable:type 相似,这是另外一种类型束缚。
如果不明确的花,看完上面这个 demo 就明确了。
type keys = 'foo' | 'bar' | 'baz' | |
const obj = { | |
foo: 'a', | |
bar: 'b', | |
baz: 'c' | |
} | |
const test = (key:any) => {return obj[key] ; // 提醒谬误 type 'any' can't be used to index type'{foo: string; bar: string; baz: string;}'. | |
} |
如何解决这个报错呢?
第一种形式:类型束缚
const test = (key:keys) => {return obj[key] ; | |
} |
第二种形式:类型断言(这种形式罕用于第三方库的 callback,返回值类型没有束缚的状况)
const test = (key:any) => {return obj[key as keys] ; | |
} |
须要留神:obj[key as keys]中 keys 的类型能够少于 obj 的类型,反过来 obj 的属性不能少于 keys 的类型。
泛型函数与泛型接口
泛型函数
想想一个场景,咱们心愿函数的输出与输入类型统一。
你可能会这样做,但这并不能保障输出与输入类型统一。
function log(value: any):any {return value;}
通过泛型函数能够精准实现:函数名后加一个 <T>
这里的 T 能够了解为泛型的名字。指定输出类型为 T,返回值为 T。
function log<T>(value: T):T {return value;}
这是一个泛型函数实例,如何定义一种泛型函数类型呢?
type Log = <T>(value: T) => T
应用泛型函数类型束缚函数:
let log : Log = function <T>(value: T):T {return value;}
泛型接口
接口所有属性灵便,输入输出统一即可。
interface Log {<T>(value: T): T | |
} | |
let myLog: Log = log | |
myLog("s")// "s" | |
myLog(1)// 1 |
接口所有属性必须为同一类型。
interface Log<T> {(value: T): T | |
} | |
let myLog: Log<string> = log | |
myLog("s")// "s" | |
myLog(1)// Error |
ts 中的<>
在 ts 中,遇到 <>
的话,尖括号两头大多状况下都是类型。
Array<string>
<string>[]
function <T>(value: T): T {...}
type MyType = <T>(value : T) => T
interface MyInterface<T> {(value: T): T }
如何了解 as const?
- 为了解决 let 赋值问题的,将一个 mutable 的变量改为 readonly。
- 防止将类型推断为联结类型。
为了解决 let 赋值问题的,将一个 mutable 的变量改为 readonly。
let x = "hello"; | |
x = "world"; // 报错 |
第一种形式 const
const x = "hello"
第二种形式 “hello” 类型
let x: "hello" = "hello"; | |
x = "world"; // |
第三种形式 discriminated unions
type Shape = | |
| {kind: "circle", radius: number} | |
| {kind: "square", sideLength: number} | |
function getShapes(): readonly Shape[] { | |
// to avoid widening in the first place. | |
let result: readonly Shape[] = [{ kind: "circle", radius: 100,}, | |
{kind: "square", sideLength: 50,}, | |
]; | |
return result; | |
} |
第四种形式 as const
.tsx 类型文件
// Type '10' | |
let x = 10 as const; | |
// Type 'readonly [10, 20]' | |
let y = [10, 20] as const; | |
// Type '{readonly text:"hello"}' | |
let z = {text: "hello"} as const; |
非.tsx 类型文件
// Type '10' | |
let x = <const>10; | |
// Type 'readonly [10, 20]' | |
let y = <const>[10, 20]; | |
// Type '{readonly text:"hello"}' | |
let z = <const>{text: "hello"}; |
优化 discriminated unions
function getShapes() { | |
let result = [{ kind: "circle", radius: 100,}, | |
{kind: "square", sideLength: 50,}, | |
] as const; | |
return result; | |
} | |
for (const shape of getShapes()) { | |
// Narrows perfectly! | |
if (shape.kind === "circle") {console.log("Circle radius", shape.radius); | |
} | |
else {console.log("Square side length", shape.sideLength); | |
} | |
} |
防止将类型推断为联结类型。
防止将类型推断为 (boolean | typeof load)[],而是推断为[boolean, typeof load]。
export function useLoading() {const [isLoading, setState] = React.useState(false); | |
const load = (aPromise: Promise<any>) => {setState(true); | |
return aPromise.finally(() => setState(false)); | |
}; | |
return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[]} |
declare global 是什么意思?
是为了在全局命名空间做申明,比方为对象减少一个未定义的属性。
为 Window 减少 csrf 的定义
declare global { | |
interface Window {csrf: string;} | |
} |
为 String 减少 fancyFormat 的定义
declare global { | |
/*~ Here, declare things that go in the global namespace, or augment | |
*~ existing declarations in the global namespace | |
*/ | |
interface String {fancyFormat(opts: StringFormatOptions): string; | |
} | |
} |
留神 global 作用域只能用于导出模块或者内部的模块申明
Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
如何在 TypeScript 环境减少一个全局变量?
比方咱们想要实现上面的成果,然而会报错 Property ‘__INITIAL_DATA__’ does not exist
<script> | |
window.__INITIAL_DATA__ = {"userID": "536891193569405430"}; | |
</script> | |
const initialData = window.__INITIAL_DATA__; // 报错 |
应用类型断言
const initialData = (window as any).__INITIAL_DATA__;
type InitialData = {userID: string;}; | |
const initialData = (window as any).__INITIAL_DATA__ as InitialData; | |
const userID = initialData.userID; // Type string |
申明全局变量
declare var __INITIAL_DATA__: InitialData;
const initialData = __INITIAL_DATA__; | |
const initialData = window.__INITIAL_DATA__; |
在 es 模块中,有 import,export 的,须要这样做:
export function someExportedFunction() {// ...} | |
declare global {var __INITIAL_DATA__: InitialData;} | |
const initialData = window.__INITIAL_DATA__; |
如果在很多文件都用到的话,能够用一个 globals.d.ts 文件。
利用 interface 合并
interface Window {__INITIAL_DATA__: InitialData;} | |
const initialData = window.__INITIAL_DATA__; |
在 js 模块中须要像上面这样:
export function someExportedFunction() {// ...} | |
declare global { | |
interface Window {__INITIAL_DATA__: InitialData;} | |
} | |
const initialData = window.__INITIAL_DATA__; |
interface 能够继承吗?
能够的。
interface Base {foo: string;} | |
interface Props extends Base { | |
bar: string | |
baz?: string | |
} | |
const test = (props: Props) => {console.log(props); | |
} | |
test({foo: 'hello'}) // Property 'bar' is missing in type '{foo: string;}' but required in type 'Props' | |
test({foo: 'hello', bar: 'world'}) |
当 Props 继承了 Base 之后,实际上它最终变成了上面这样:
interface Props extends Base { | |
foo: string; | |
bar: string | |
baz?: string | |
} |
Props 能够笼罩 Base 吗?能够,然而只能是 required 笼罩 optional,optional 不能笼罩 required。
// ✅ | |
interface Base {foo?: string;} | |
interface Props extends Base { | |
foo: string; | |
bar: string | |
baz?: string | |
} |
// ❌ | |
interface Base {foo: string;} | |
interface Props extends Base { | |
foo?: string; | |
bar: string | |
baz?: string | |
} |
typescript 中的 & 是什么意思?
在 react 的 dts 文件中有这样一个定义。
type PropsWithChildren<P> = P & {children?: ReactNode};
typescript 中的 & 指的是穿插类型。
interface ErrorHandling { | |
success: boolean; | |
error?: {message: string}; | |
} | |
interface ArtworksData {artworks: { title: string}[];} | |
interface ArtistsData {artists: { name: string}[];} | |
// These interfaces are composed to have | |
// consistent error handling, and their own data. | |
type ArtworksResponse = ArtworksData & ErrorHandling; | |
type ArtistsResponse = ArtistsData & ErrorHandling; | |
const handleArtistsResponse = (response: ArtistsResponse) => {if (response.error) {console.error(response.error.message); | |
return; | |
} | |
console.log(response.artists); | |
}; |
晓得 & 是 ts 中的穿插类型当前,咱们就明确 PropsWithChildren 的意思了,而且也明确为什么 react 的函数式组件会比一般函数组件多了 children 属性。
它的意思是 PropsWithChildren 类型是 P 和对象 {children?: ReactNode} 的穿插类型,也就是通过 & 连贯两个对象之后,最终生成的对象是领有 children 这个可选属性的。
interface 与 type 的区别是什么?
An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot.
An interface can have multiple merged declarations, but a type alias for an object type literal cannot.
- interface 能够继承(比方用 extends),type 不能够
- interface 能够实现有多个合并申明,type 不能够
enum 作为一种类型是什么意思?
在浏览 pixi.js 的源码中,发现有将 enum 作为了一种类型。
enum 也能够作为一种类型去束缚。
// pixi/constants | |
export enum BLEND_MODES { | |
NORMAL = 0, | |
ADD = 1, | |
MULTIPLY = 2, | |
SCREEN = 3, | |
OVERLAY = 4, | |
} | |
export enum ANOTHER_ENUM { | |
FOO = 5, | |
BAR = 6 | |
} | |
import {BLEND_MODES} from '@pixi/constants'; | |
export class Sprite extends Container | |
{ | |
public blendMode: BLEND_MODES; | |
constructor(){ | |
this.blendMode = BLEND_MODES.NORMAL; // 最佳 | |
// this.blendMode = 0 这样是能够的,次之 | |
// this.blendMode = ANOTHER_ENUM.FOO 这样 ts 会报错 | |
} | |
} |
我的项目中 xxx.d.ts 的 declare module ‘*.scss’ 是什么意思?declare module
还能够做什么?
我的项目中 xxx.d.ts 的 declare module ‘*.scss’ 是什么意思?
// externals.d.ts | |
declare module '*.scss' |
默认状况下 import style from ‘style.scss’ 在 ts 的 ide 校验器里会报错,那就用 d.ts 假设定义所有 scss 结尾的文件是 module。– 社长
假如将 declare module ‘*.scss’ 正文掉,ide 会报错,然而能够通过 lint。
declare module
还能够做什么?
当咱们引入了一个微软官网 @types/* 中不存在的自定义包时,ide 会报错。
例如上面这样:
如何解决这个报红的谬误呢?declare module
// typing.d.ts | |
declare module 'visual-array' |
这样报红就隐没了。
typescript 如何束缚 Promise 的类型?
Promise 泛型函数
interface Promise<T> {then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>; | |
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>; | |
} |
interface foo {bar: ()=>Promise<string>, | |
baz: ()=>Promise<number[]>, | |
car: (id)=>Promise<boolean[]>} |
typescript 中的 keyof 如何应用?
最简
type Point = {x: number; y: number}; | |
type P = keyof Point; // 'x' | 'y' | |
let foo: P = 'x'; | |
let bar: P = 'y'; | |
let baz: P = 'z'; // ❌ |
罕用
interface Person { | |
name: string; | |
age: number; | |
location: string; | |
} | |
type K1 = keyof Person; // "name" | "age" | "location" | |
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ... | |
type K3 = keyof {[x: string]: Person }; // string | |
type P1 = Person["name"]; // string | |
type P2 = Person["name" | "age"]; // string | number | |
type P3 = string["charAt"]; // (pos: number) => string | |
type P4 = string[]["push"]; // (...items: string[]) => number | |
type P5 = string[][0]; // string |
keyof 使得函数类型平安(type-safe)
function getProperty<T, K extends keyof T>(obj: T, key: K) {return obj[key]; // Inferred type is T[K] | |
} | |
function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]) {obj[key] = value; | |
} | |
let x = {foo: 10, bar: "hello!"}; | |
let foo = getProperty(x, "foo"); // number | |
let bar = getProperty(x, "bar"); // string | |
let oops = getProperty(x, "wargarbl"); // Error! "wargarbl" is not "foo" | "bar" | |
setProperty(x, "foo", "string"); // Error!, string expected number |
Partial,Required,Readonly,Pick 泛型工具类型的实现原理
type Partial<T> = {[P in keyof T]? : T[P]; | |
} |
type Required<T> = {[P in keyof T]?- : T[P]; | |
} |
type Readonly<T> = {readonly [P in keyof T] : T[P]; | |
} |
type Pick<T, K extends keyof T> = {[P in K]: T[P] | |
} |
typescript 中的 typeof 如何应用?
js 中的 typeof 次要用于表达式上下文,而 ts 中的 typeof 次要用于类型上下文。
let s = "hello"; | |
let n: typeof s; | |
// ^ = let n: string |
type Predicate = (x: unknown) => boolean; | |
type K = ReturnType<Predicate>; | |
// ^ = type K = boolean |
function f() {return { x: 10, y: 3}; | |
} | |
type P = ReturnType<typeof f>; | |
// ^ = type P = { | |
// x: number; | |
// y: number; | |
// } |
typescript 中的 non-null assert operator
是什么?
非 null 断言操作符:当为 null 时,产生断言,抛出异样。
可选链:当为 null/undefined 时,返回 undefined。
非空断言操作符和可选链操作符测试
// Non-Null Assertion Operator | |
const obj = null; | |
interface Entity {name?: string;} | |
// 非空断言操作符 | |
function nonNull(e?: Entity) {const s = e!.name; // 产生断言,抛出 TypeError} | |
try {nonNull(obj); | |
} catch (e) {console.error("nonNull catch", e); // TypeError: Cannot read property 'name' of null | |
} | |
// 可选链 | |
function optionalChaining(e?: Entity) { | |
const s = e?.name; | |
console.log(s); // undefined | |
} | |
optionalChaining(obj); |
用于函数返回值空检测
function returnNullFunc() {return null;} | |
try {returnNullFunc()!.age; | |
} catch (e) {console.error("returnNullFunc", e); // TypeError: Cannot read property 'age' of null | |
} | |
function returnNonNullFunc() { | |
return {age: "18"}; | |
} | |
returnNonNullFunc()!.age; |
在线 demo:https://codesandbox.io/s/non-…
期待和大家交换,共同进步:
- 微信公众号:大大大前端 / excellent_developers
- Github 博客: 趁你还年老 233 的集体博客
- SegmentFault 专栏:趁你还年老,做个优良的前端工程师
- 掘金账号:趁你还年老 233
致力成为优良前端工程师!