typescript开发心得

语法知识点

回调地狱问题

用await,或者有些库提供了sync方法

yield

用法跟python的一样。

yield只能用于生成器里,生成器是function*,例如:

export function* filter(rootNode: ts.Node, acceptedKind: ts.SyntaxKind) {
    for (const iterObj of walkTree(rootNode)) {
        if (iterObj[1].kind == acceptedKind) {
            yield iterObj;
        }
    }
}

字符串插值

ts没有format函数,用``插值,样例如下:

let s = "aaa";

// 字符串插值
console.log(`i love ${s.length}`);

写文件

流式写:

function printAst(rootNode: ts.Node, outputFile: string) {
    const wstream = fs.createWriteStream(outputFile);
    try {
        for (...) {
            ...
            wstream.write(`${new Array(path.length + 1).join('----')} ${syntaxKindToName(node.kind)} ${node.pos} ${node.end}\n`);
        }
        wstream.end();
    } finally {
        wstream.close();
    }
}

一次性写就用fs.writeFile

const/var/let的区别

const是常量,声明时初始化,不能修改。

let是变量,有块级作用域,超出块级作用域就会无法访问。

var是变量,它的作用域是当前执行上下文。

set集合

加入元素用add,判断元素用has。

用法如下:

const funcNames = new Set();
for (const iterObj of tsFileParser.filter(sourceFile, ts.SyntaxKind.CallExpression)) {
    let node = iterObj.current as ts.CallExpression;
    funcNames.add(getCallFuncName(node));
}
expect(funcNames.has("f")).toBe(true);

多变量赋值的写法

let beginPos = -1, endPos = -1;

顺带说一下,局部变量未声明初始值的话,值为undefined

列表切片

用slice方法

var students = ['killer', 'bear', 'tiger']
console.log(students.slice(1, -1))

as

类型断言,类似java里的downcast,例如:

let node = iterObj[1] as ts.StringLiteral;
console.log(node.pos, node.text)

正则表达式

正则有两种写法,一是斜杠字面量声明:

const pat = /<script\s+lang="ts"\s+setup>([\s\S]*?)<\/script>/g;

一是RegExp类构造声明:

const PAT_CN = new RegExp("[\u4e00-\u9fa5]");

RegExp有两个方法:test和exec,前者仅判断是否匹配,返回bool。后者返回匹配对象,类似于python里的match object。

完整样例:

test('test regex', () => {
    // 这里我们要匹配换行符在内的任意字符,点号不行,需用[\s\S]
    const pat = /<script\s+lang="ts"\s+setup>([\s\S]*?)<\/script>/g;
    const s = "<script lang=\"ts\" setup>\n" +
        "console.log(\"1\");\n" +
        "</script>" +
        "aaa    " +
        "<script lang=\"ts\" setup>\n" +
        "console.log(\"2\");\n" +
        "</script>";

    const conts = []
    let match;
    //这里需用while循环,因RegExp.exec只会返回一次匹配,要获得所有匹配,得做循环
    // match[0]是匹配的全文,match[1]是捕获的文本
    while ((match = pat.exec(s)) !== null) {
        conts.push(match[1]);
    }
    console.log(conts);
    expect(conts.length).toBe(2);
})

package.json

scripts字段含义

"scripts": {
    "build": "tsc",
    "test": "jest"
  },

用npm run执行scripts里的命令:

npm run build
npm run test

顺带说一下,要支持typescript和jest,要在package.json里配置:

"devDependencies": {
    "@types/node": "^22.14.0",
    "typescript": "^5.5.3",
    "@types/jest": "^29.5.14",    
    "jest": "^29.7.0",
    "ts-jest": "^29.3.1"
  },

UT

使用jest。

默认情况下jest只识别js文件,需要对ts进行转译让jest识别。否则报错:

Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

配置jest.config.js如下:

module.exports = {
    // 预设库
    preset: 'ts-jest',
    // jest模拟的环境,可以选择node、jsdom来模拟node和浏览器环境
    testEnvironment: 'node',
    // 待测试的文件路径
    testMatch: ['<rootDir>/tests/**/*.test.ts'],
    moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
    // 转义插件,方便jest理解代码
    transform: {
        '^.+\\.ts$': 'ts-jest',
    },
    // 不进行匹配的目录
    transformIgnorePatterns: ['<rootDir>/node_modules/'],
    // 处理 webpack 的别名,比如:将 @ 表示 根目录
    moduleNameMapper: {
        '^@/(.*)$': '<rootDir>/$1',
    },
};

要支持typescript下的UT,需在package.json里配置jest和ts-jest:

"devDependencies": {
    ...
    "@types/jest": "^29.5.14",    
    "jest": "^29.7.0",
    "ts-jest": "^29.3.1"
  },

日志打印

使用winston:

export const LOGGER = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format.timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
        winston.format.printf(({timestamp, level, message}) => {
            return `${timestamp} [${level}]: ${message}`;
        })
    ),
    transports: [
        new winston.transports.File({filename: 'website_checker.log', options: {flags: 'w'}})
    ]
});

使用combine format,打印时间戳、级别及信息。options: {flags: ‘w’}表示每次覆盖日志文件,默认是追加。

类的写法跟java很像,几乎不用额外学习。例子:

class StaticChecker {
    private rootDir: string;

    constructor(rootDir: string) {
        this.rootDir = rootDir;
    }

    // 普通成员
    public execute() {
        ...
    }
        
    // 生成器成员
    * walkTree(rootNode: ts.Node): Generator<VisitResult> {
        yield {path: [], current: rootNode};

        ...
    }   

    ...
}

const checker = new StaticChecker("proj_path");
checker.execute();

类的静态方法

方法前加static即可。

export class TsParseUtils {
    static getCallFuncName(node: ts.CallExpression): string | null {
        let ownerNode = node.getChildAt(0);
        let secondNode = node.getChildAt(1);
        if (secondNode.kind == ts.SyntaxKind.OpenParenToken) {
            return ownerNode.getText();
        } else {
            return null;
        }
    }

正式代码里支持@符号

在tsconfig.json里配置paths选项:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "outDir": "dist",
    // 支持import时的@符号
    "paths": {
      "@/*": [
        "./*"
      ]
    }
  },
  "include": [
    "src"
  ]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值