写了3个月TypeScript,我学到了什么?

TypeScript封装axios——Vue3+TypeScript实践 TypeScript封装axios简介1. 认识axios1.1 为什么选择axios2. axios基本使用3. 用面向对象的思想封装axios3.1 为什么要对axios进行封装?3.2 用面向对象的思想封装axios3.2.1 基础封装3.2.2 支持传入自定义拦截器功能4. 使用封装后的HRequest 简介 最近在用Vue3 + TypeScript 重构一个Vue2项目,之前项目中用到axios来发送网络请求,进行前后端交互,但并未对axios库做过多的封装,导致代码重复度较高,维护起来比较麻烦 阅读详情

写了3个月TypeScript,我学到了什么?.png

原文链接: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/docs/handbook/classes.html
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.

  1. interface可以继承(比如用extends),type不可以
  2. 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会报错。

例如下面这样:
image

如何解决这个报红的错误呢?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-null-operator-6bcf4

期待和大家交流,共同进步:

努力成为优秀前端工程师!

如何在Vue3项目中引入TypeScript 在这篇文章中,我们将一步步指导你如何在Vue3项目中引入TypeScript。TypeScript是一种强类型的JavaScript超集,可以帮助我们在编写代码时避免许多错误,提高代码的可维护性。在Vue组件中使用TypeScript非常简单,只需要在``标签中添加`lang="ts"`。为了让TypeScript更好地发挥作用,需要安装一些类型声明文件。至此,您已经成功地在Vue3项目中引入了TypeScript。按照提示进行初始化配置,选择手动配置,并确保选择Vue 3选项。 阅读详情

相关推荐

如何在 Nuxt 3 中有效使用 TypeScript

通过上述步骤,你已经成功启用和使用 TypeScript,在 Nuxt 3 项目中进行类型检查,并创建一个简单的 TypeScript 组件。TypeScript的强大类型系统将帮助你在编写代码时更好地发现潜在错误,提升开发体验。编程智域 前端至全栈交流与成长如何在 Nuxt 3 中有效使用 TypeScript | cmdragon’s Blog。

https://blog.cmdragon.cn/ 1380

typescript as unknown as 可能解释简记

as unknown as 是用来代替 as any的开发中经常会遇到类型定义的不太好

柠檬人的博客 5601

Vue3通透教程【十一】初探TypeScript

Vue3对TS大力支持,TS也将成为Vue开发中的必要技能,从本篇文章开始将带领大家通过几节文章来掌握TS基础

商务合作欢迎私信我 1万+

TypeScript中的as unknown as xxx的用法及目的

目的: 类型断言,是一种可能解释,强制转换为后面的一种格式

小多的博客 3261

对话系统简介

《文本上的算法》学习笔记1、问答型1.1、基于语义分析的方法1.2、基于信息抽取的方法1.3、端对端的方法2、任务型2.1、生成式模型( Generative Model )2.2、判别式模型( Discrimitive Model )2.3、规则系统3、闲聊型3.1、规则方法3.2、生成模型3.3、检索方法 个人认为NLP领域最难的方向可以说是阅读理解与文本生成/摘要,而对话系统,尤其是闲聊常常...

盘一哈nlp 6752

HFSS学习笔记——Vivaldi天线仿真(一)

Vivaldi 天线HFSS仿真,HFSS中指数曲线绘制及曲面绘制。

and_future的博客 1万+

TS学习笔记(二)

完整的声明格式如下:声明了类型后TypeScript就会进行类型检测,声明的类型可以称之为;=赋值;(ps:在tslint中并不推荐使用var来声明变量,主要原因和ES6升级后let和var的区别是一样的,var没块级作用域会引起很多的问题)数据类型是。

m0_52711377的博客 1887

CPG控制网络入门

从线性变换开始推导

妖米的博客 5044

TypeScript 基础精粹

类型注意事项 数组类型 有两种类型注解方式,特别注意第二种使用 TS 内置的 Array 泛型接口。 let arr1: number[] = [1,2,3] // 下面就是使用 TS 内置的 Array 泛型接口来实现的 let arr2: Array<number | string> = [1,2,3,"abc"] 元组类型 元组是一种特殊的数组,限定了数组元素的个数和类型 lettuple: [number,string] = [0,"1"]; 需要注意元组的越界问...

JNPF快速开发平台的博客 1083

保姆级拆解:HP 1312NF/CM1415打印机进纸离合器清洁与更换全记录(附B站/油管视频指路)

本文详细记录了HP 1312NF打印机进纸离合器故障的诊断与修复全过程,包括故障判断、工具准备、拆解步骤、清洁更换技巧及预防措施。特别针对离合器内部海绵老化粘连问题,提供了电工胶布和硬纸片两种实用替代方案,并附有B站/油管视频指路,帮助用户轻松完成打印机维修。

weixin_31102231的博客 375

Qt5.15.2安卓开发环境配置保姆级教程(含JDK8、SDK33、NDK21.4及Gradle国内镜像避坑指南)

本文提供Qt5.15.2安卓开发环境配置的完整教程,涵盖JDK8、SDK33、NDK21.4及Gradle国内镜像设置,帮助开发者快速搭建开发环境并避免常见问题。通过详细步骤和实战技巧,解决依赖冲突、网络超时等难题,提升开发效率。

weixin_28730353的博客 203

AS--Unknown Device(Device support,but APK only supports armeabi-v7a)

AS--Unknown Device(Device support,but APK only supports armeabi-v7a)

model2005的专栏 1123

图解B树的原理及操作

本文主要参考:https://blog.youkuaiyun.com/shenchaohao12321/article/details/83243314 1. B树 1.1 B树的定义 B树也称B-树,它是一颗多路平衡查找树。我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数。当m取2时,就是我们常见的二叉搜索树。 一颗m阶的B树定义如下: 1)每个结点最多有m-1个关键字。 2)根结点最少可以只有1个关键字。 3)非根结点至少有Math.ceil(m/2)-

jinking01的专栏 3万+

Vue3+TypeScript?看这一篇就够了

TypeScript和Vue3组合Api学习文档

淮城一只猫 4万+

配置xen上VM混杂模式,使其能捕获物理网络流量

本文目的:配置VM和xen相应的物理网卡(pif)和虚拟网卡(vi

LeoDuo的专栏 3720

Windows下Delft3D安装全攻略:从申请许可到编译运行(避坑指南)

本文详细解析了Windows系统下Delft3D的完整安装流程,涵盖环境准备、许可获取、源码编译到性能调优的全过程。针对中国用户常见的网络问题和系统兼容性痛点,提供了经过验证的解决方案,特别强调Intel Fortran编译器匹配、环境变量配置等关键步骤,帮助水利工程师高效完成这一水动力学模拟工具的部署。

weixin_28736355的博客 548

TypeScript及TypeScript在vue3.0项目中的基本使用

一、 TypeScript是什么 TypeScript 是一种由微软开发的自由开源的编程语言,主要提供了类型系统和对 ES6的支持。它是JavaScript的一个超集,扩展了JavaScript的 语法,因此现有的 JavaScript 代码可与TypeScript 一起工作无需任何修改。 TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript可以运行在任何浏览器上。 TypeScript 是 Angular2、vue3的开发语言

Jeasu_0908的博客 2万+

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Vue3+TypeScript 完整项目上手教程

转自:Vue3拥抱TypeScript的正确姿势 :https://juejin.im/post/6875713523968802829一个完整的Vue3+Ts项目,支持.vue和.ts...

程序员成长指北 4万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

我们花了 3 个月迁移到 TypeScript,这是值得的吗?

TypeScript 一开始就抱怨:"你这不是真正的 User 类型。更重要的是,那些数据结构相关的 bug——就是那些"字段名不匹配"、"类型错了"的 bug——几乎消失了。那一周里,我们修了三个类似的"跨界 bug"。有人说"好主意",也有人说"不就是加个冒号吗,有那么重要?于是,我们用 3 个月完成了从 JavaScript 到 TypeScript 的迁移。TypeScript 不会让你成为更聪明的程序员,但它会让你更自信。类型签名自动文档化了接口,我们不用反复问"这个参数是什么"。

前端达人 604

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Vue3 携手 TypeScript 搭建完整项目结构

TypeScript 是JS的一个超集,主要提供了类型系统和对ES6的支持,使用 TypeScript 可以增加代码的可读性和可维护性,在 react 和 vue 社区中也越来越多人开始使用TypeScript。从最近发布的 Vue3 正式版本来看, Vue3 的源码就是用 TypeScript 编写的,更好的 TypeScript 支持也是这一次升级的亮点。当然,在实际开发中如何正确拥抱 TypeScript 也是迁移至 Vue3 的一个小痛点,这里就针对 Vue3 和 TypeScript 展开一些交流

功能总结 6052

Vue3使用TypeScript的正确姿势

作者:TinssonTaihttps://juejin.im/post/6875713523968802829一个完整的Vue3+Ts项目,支持.vue和.tsx写法项目地址:https...

前端开发博客 2万+

Vite+Vue3+TypeScript

vite诞生是为了提升web项目运行性能,以更快的速度将应用页面展示给用户。Vite 以原生 ESM方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。提供的驱动力:2.1、优化缓慢的服务器启动(冷启动开发服务器和正式环境响应速度);2.2、优化缓慢的项目更新(vite服务器);

云 15万+

Vue 3系列之03——Vue 3使用TypeScript

随着应用规模的增长,越来越多的开发者认识到静态语言的好处。静态类型系统可以帮助防止许多潜在的运行时错误,这就是为什么Vue 3是用TypeScript编写的。这意味着在Vue应用开发中,使用TypeScript进行开发不需要任何其他工具。TypeScript在Vue 3世界中是一等公民。 有两种方式,可以实现在Vue 3应用中支持使用TypeScript。 1.基于Vue 3 Preview创建的项目 如果是选择Vue 3模板“Vue 3 Previe...

老卫的技术站 1万+

Vue3 TypeScript集成

<ul></li></ul>

Flying_Fish_roe的博客 2257

如何将 Vue 3 与 TypeScript 一起使用

Vue 是一个令人惊叹的轻量级、渐进式前端框架。在本教程中,我们将演示如何完全使用 TypeScript 构建 Vue 应用程序。我们将通过使用基于类的组件、用于状态管理的 Vuex、生命周期钩子等构建示例应用程序,重点介绍将 TypeScript 与 Vue 结合使用的一些好处。让我们开始吧!Vue 和 TypeScript 入门如何将 TypeScript 添加到 Vue 项目中使用defineComponent使用数据使用道具计算属性方法观察者emit导入组件。

分享身边生活经验blog 2345

vue3.0加上Typescript

vue3.0加上Typescript 1、使用npm或者yarn进行vue项目的搭建 //创建项目 vue create test cd test //下载依赖 npm install axios //或者 yarn add axios //启动项目 npm run serve //或者 yarn serve //项目打包 npm run build //或者 yarn build 创建好项目之后,使用vue3.0的特性进行项目构建,vue3.0中引入了一个setup()函数进行统一管理vue的data、

enhengcys的博客 3923
上一篇: 发现算法之美-时间复杂度
下一篇: 写了3个月React,我学到了什么?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

趁你还年轻233

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值