ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

过去 3 年的所有 JavaScript 和 TypeScript 功能

2023-03-11 11:52:49  阅读:377  来源: 互联网

标签:JavaScript 网站开发 编程


ECMAScript

过去(仍然相关的旧介绍)

  • 标记的模板文本:通过在模板文本前面加上函数名称,函数将传递模板文本的各个部分和模板值。这有一些有趣的用途。
假设我们想编写一种方法来记录包含数字的任意字符串,但格式化数字。
我们可以为此使用标记模板。
函数格式数字(字符串:模板字符串数组,数字:数字):字符串{返回字符串
[0] +数字。到固定(2) + 字符串[1];
}
控制台。log(formatNumbers'这是值: ${0}, 这很重要。这是值:0.00,这很重要。

或者,如果我们想“翻译”(此处更改为小写)字符串中的翻译键。
函数翻译键(键:字符串):字符串{ 
 返回键。toLocaleLowerCase();
}
function translate(strings: TemplateStringsArray, ...表达式:字符串[]):字符串{
返回字符串。reduce((累加器, currentValue, index) => 累加器 + currentValue + translateKey(expressions[index] ??“”)、“);
}
控制台。log(翻译'Hello, this is ${'NAME'} 表示${'MESSAGE'}.');您好,这是要说消息的名字。
  • 符号:对象的唯一键:。内部使用。Symbol("foo") === Symbol("foo"); // false
const obj: { [index: string]: string } = {}; 

常量符号A = 符号('a');
常量符号B = 符号。对于(“b”);

控制台。log(符号 A.描述);// “a”

obj[symbolA] = 'a';
obj[symbolB] = 'b';
obj['c'] = 'c';
obj.d = 'd';

控制台。log(obj[symbolA]);“a”
控制台。log(obj[symbolB]);// “b”
// 密钥不能使用任何其他符号或没有符号访问。
控制台。log(obj[Symbol('a')]);未定义的
控制台。log(obj['a']);// 未定义

// 使用 ...在。
for (const i in obj) {
 console.日志(i);“c”, “d”
}

ES2020

  • 可选链接:若要访问可能未定义对象的值(通过索引),可以在父对象名称后使用可选链接。这也可以用于索引 () 或函数调用。?[...]
// 以前:
// 如果我们有一个对象变量(或任何其他结构),我们不确定是否定义,
// 我们无法轻松访问该属性。
常量对象: { 名称: 字符串 } |未定义 = 数学。随机() > 0.5 ?未定义 : { 名称: '测试' };
常量值 = 对象。姓名;类型错误:“对象”可能是“未定义”。

我们可以先检查它是否已定义,但这会影响可读性,并且对于嵌套对象来说变得复杂。
const objectOld: { name: string } |未定义 = 数学。随机() > 0.5 ?未定义 : { 名称: '测试' };
常量值老 = 对象老 ?对象旧。名称 : 未定义;

相反,
我们可以使用可选的链接。
const 对象新: { 名称: 字符串 } |未定义 = 数学。随机() > 0.5 ?未定义 : { 名称: '测试' };
常量值新 = 对象新?。姓名;

这也可以用于索引和函数。
常量数组:字符串[] |未定义 = 数学。随机() > 0.5 ?未定义:[“测试”];
常量项目 = 数组?。[0];
常量函数: (() => 字符串) |未定义 = 数学。随机() > 0.5 ?未定义 : () => '测试';
常量结果 = 函数?。();
  • Nullish coalescing operator (??): Instead of using the operator for conditionally assigning, the new operator can be used. Instead of applying to all falsy values, it only applies to and .||??undefinednull
const value: string | undefined = Math.random() > 0.5 ? undefined : 'test';

// PREVIOUSLY:
// When we want to conditionally assign something else if a value is undefined or null, we could use the "||" operator.
const anotherValue = value || 'hello';
console.log(anotherValue); // "test" or "hello"

// This works fine when using truthy values, but if we were to compare to 0 or an empty string, it would also apply.
const incorrectValue = '' || 'incorrect';
console.log(incorrectValue); // always "incorrect"
const anotherIncorrectValue = 0 || 'incorrect';
console.log(anotherIncorrectValue); // always "incorrect"

// NEW:
// Instead we can use the new nullish coalescing operator. It only applies to undefined and null values.
const newValue = value ?? 'hello';
console.log(newValue) // always "hello"

// Now falsy values are not replaced.
const correctValue = '' ?? 'incorrect';
console.log(correctValue); // always ""
const anotherCorrectValue = 0 ?? 'incorrect';
console.log(anotherCorrectValue); // always 0
  • import(): Dynamically import, just like , but at runtime and using variables.import ... from ...
let importModule;
if (shouldImport) {
  importModule = await import('./module.mjs');
}
  • String.matchAll: Get multiple matches of a regular expression including their capture groups, without using a loop.
const stringVar = 'testhello,testagain,';

// PREVIOUSLY:
// Only gets matches, but not their capture groups.
console.log(stringVar.match(/test([\w]+?),/g)); // ["testhello,", "testagain,"]

// Only gets one match, including its capture groups.
const singleMatch = stringVar.match(/test([\w]+?),/);
if (singleMatch) {
  console.log(singleMatch[0]); // "testhello,"
  console.log(singleMatch[1]); // "hello"
}

// Gets the same result, but is very unintuitive (the exec method saves the last index).
// Needs to be defined outside the loop (to save the state) and be global (/g),
// otherwise this will produce an infinite loop.
const regex = /test([\w]+?),/g;
let execMatch;
while ((execMatch = regex.exec(stringVar)) !== null) {
  console.log(execMatch[0]); // "testhello,", "testagain,"
  console.log(execMatch[1]); // "hello", "again"
}

// NEW:
// Regex needs to be global (/g), also doesn't make any sense otherwise.
const matchesIterator = stringVar.matchAll(/test([\w]+?),/g);
// Needs to be iterated or converted to an array (Array.from()), no direct indexing.
for (const match of matchesIterator) {
  console.log(match[0]); // "testhello,", "testagain,"
  console.log(match[1]); // "hello", "again"
}
  • Promise.allSettled(): Like , but waits for all Promises to finish and does not return on the first reject/throw. It makes handling all errors easier.Promise.all()
async function success1() {return 'a'}
async function success2() {return 'b'}
async function fail1() {throw 'fail 1'}
async function fail2() {throw 'fail 2'}

// PREVIOUSLY:
console.log(await Promise.all([success1(), success2()])); // ["a", "b"]
// but:
try {
  await Promise.all([success1(), success2(), fail1(), fail2()]);
} catch (e) {
  console.log(e); // "fail 1"
}
// Notice: We only catch one error and can't access the success values.

// PREVIOUS FIX (really suboptimal):
console.log(await Promise.all([ // ["a", "b", undefined, undefined]
  success1().catch(e => { console.log(e); }),
  success2().catch(e => { console.log(e); }),
  fail1().catch(e => { console.log(e); }), // "fail 1"
  fail2().catch(e => { console.log(e); })])); // "fail 2"

// NEW:
const results = await Promise.allSettled([success1(), success2(), fail1(), fail2()]);
const sucessfulResults = results
  .filter(result => result.status === 'fulfilled')
  .map(result => (result as PromiseFulfilledResult<string>).value);
console.log(sucessfulResults); // ["a", "b"]
results.filter(result => result.status === 'rejected').forEach(error => {
  console.log((error as PromiseRejectedResult).reason); // "fail 1", "fail 2"
});
// OR:
for (const result of results) {
  if (result.status === 'fulfilled') {
    console.log(result.value); // "a", "b"
  } else if (result.status === 'rejected') {
    console.log(result.reason); // "fail 1", "fail 2"
  }
}
  • BigInt: The new data type allows for accurately storing and operating on large (whole) numbers, which prevents errors produced by JavaScript storing numbers as floats. They can either be constructed using the constructor (preferably with strings to prevent inaccuracies) or by appending at the end of a number.BigIntBigInt()n
// PREVIOUSLY:
// JavaScript stores numbers as floats, so there is always a bit of inaccuracy
// but more importantly, there start to be inaccuracies for integer operations after a certain number.
const maxSafeInteger = 9007199254740991;
console.log(maxSafeInteger === Number.MAX_SAFE_INTEGER); // true

// If we compare numbers bigger than it, there can be inaccuracies.
console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2);

// NEW:
// Using the new BigInt datatype we can in theory store and operate on infinitely big (whole) numbers.
// Use it by using the BigInt constructor or appending "n" at the end of a number.
const maxSafeIntegerPreviously = 9007199254740991n;
console.log(maxSafeIntegerPreviously); // 9007199254740991

const anotherWay = BigInt(9007199254740991);
console.log(anotherWay); // 9007199254740991

// If we use the constructor using numbers, we can not safely pass integers bigger than the MAX_SAFE_INTEGER.
const incorrect = BigInt(9007199254740992);
console.log(incorrect); // 9007199254740992
const incorrectAgain = BigInt(9007199254740993);
console.log(incorrectAgain); // 9007199254740992
// Oops, they convert to the same value.

// Instead use strings or better the other syntax.
const correct = BigInt('9007199254740993');
console.log(correct); // 9007199254740993
const correctAgain = 9007199254740993n;
console.log(correctAgain); // 9007199254740993

// hex, octal and binary numbers can also be passed as strings.
const hex = BigInt('0x1fffffffffffff');
console.log(hex); // 9007199254740991
const octal = BigInt('0o377777777777777777');
console.log(octal); // 9007199254740991
const binary = BigInt('0b11111111111111111111111111111111111111111111111111111');
console.log(binary); // 9007199254740991

// Most arithmetic operations work just like you would expect them to,
// though the other operator needs to also be a BigInt. All operations return BigInts as well.
const addition = maxSafeIntegerPreviously + 2n;
console.log(addition); // 9007199254740993

const multiplication = maxSafeIntegerPreviously * 2n;
console.log(multiplication); // 18014398509481982

const subtraction = multiplication - 10n;
console.log(subtraction); // 18014398509481972

const modulo = multiplication % 10n;
console.log(modulo); // 2

const exponentiation = 2n ** 54n;
console.log(exponentiation); // 18014398509481984

const exponentiationAgain = 2n^54n;
console.log(exponentiationAgain); // 18014398509481984

const negative = exponentiation * -1n;
console.log(negative); // -18014398509481984

// Division works a bit differently, since BigInt can only store whole numbers.
const division = multiplication / 2n;
console.log(division); // 9007199254740991
// For whole numbers that are divisible, this works just fine.

// But for numbers that are not divisible, this will act like integer division (rounded down).
const divisionAgain = 5n / 2n;
console.log(divisionAgain); // 2

// There is no strict equality (but loose equality) to non-BigInt numbers.
console.log(0n === 0); // false
console.log(0n == 0); // true

// But comparisons work as expected.
console.log(1n < 2); // true
console.log(2n > 1); // true
console.log(2 > 2); // false
console.log(2n > 2); // false
console.log(2n >= 2); // true

// They are of the type "bigint"
console.log(typeof 1n); // "bigint"

// They can be converted back to regular numbers (signed and unsigned (no negative numbers)).
// Though this of course sacrifices the accuracy. The number of significant digits can be specified.

console.log(BigInt.asIntN(0, -2n)); // 0
console.log(BigInt.asIntN(1, -2n)); // 0
console.log(BigInt.asIntN(2, -2n)); // -2
// Usually you would use a higher number of bits.

// Negative numbers will be converted to the 2's-complement when converting to an unsigned number.
console.log(BigInt.asUintN(8, -2n)); // 254
  • globalThis: Access variables in the global context, regardless of the environment (browser, Node.js, …). Still considered bad practice, but sometimes necessary. Akin to at the top level in the browser.this
console.log(globalThis.Math); // Math Object
  • import.meta: When using ES-modules, get the current module URL .import.meta.url
console.log(import.meta.url); // "file://..."
  • export * as … from …: Easily re-export defaults as submodules.
export * as am from 'another-module'
import { am } from 'module'

ES2021

  • String.replaceAll(): Replace all instances of a substring in a string, instead of always using a regular expression with the global flag (/g).
const testString = 'hello/greetings everyone/everybody';
// PREVIOUSLY:
// Only replaces the first instance
console.log(testString.replace('/', '|')); // 'hello|greetings everyone/everybody'

// Instead a regex needed to be used, which is worse for performance and needs escaping.
// Not the global flag (/g).
console.log(testString.replace(/\//g, '|')); // 'hello|greetings everyone|everybody'

// NEW:
// Using replaceAll this is much clearer and faster.
console.log(testString.replaceAll('/', '|')); // 'hello|greetings everyone|everybody'
  • Promise.any: When only one result of a list of promises is needed, it returns the first result, it only rejects when all promises reject and returns an , instead of , which instantly rejects.AggregateErrorPromise.race
async function success1() {return 'a'}
async function success2() {return 'b'}
async function fail1() {throw 'fail 1'}
async function fail2() {throw 'fail 2'}

// PREVIOUSLY:
console.log(await Promise.race([success1(), success2()])); // "a"
// but:
try {
  await Promise.race([fail1(), fail2(), success1(), success2()]);
} catch (e) {
  console.log(e); // "fail 1"
}
// Notice: We only catch one error and can't access the success value.

// PREVIOUS FIX (really suboptimal):
console.log(await Promise.race([ // "a"
  fail1().catch(e => { console.log(e); }), // "fail 1"
  fail2().catch(e => { console.log(e); }), // "fail 2"
  success1().catch(e => { console.log(e); }),
  success2().catch(e => { console.log(e); })]));

// NEW:
console.log(await Promise.any([fail1(), fail2(), success1(), success2()])); // "a"
// And it only rejects when all promises reject and returns an AggregateError containing all the errors.
try {
  await Promise.any([fail1(), fail2()]);
} catch (e) {
  console.log(e); // [AggregateError: All promises were rejected]
  console.log(e.errors); // ["fail 1", "fail 2"]
}
  • Nullish coalescing assignment (??=): Only assign a value when it was “nullish” before (null or undefined).
let x1 = undefined;
let x2 = 'a';
const getNewValue = () => 'b';

// Assigns the new value to x1, because undefined is nullish.
x1 ??= 'b';
console.log(x1) // "b"

// Does not assign a new value to x2, because a string is not nullish.
// Also note: getNewValue() is never executed.
x2 ??= getNewValue();
console.log(x1) // "a"
  • Logical and assignment (&&=): Only assign a value when it was “truthy” before (true or a value that converts to true).
let x1 = undefined;
let x2 = 'a';
const getNewValue = () => 'b';

// Does not assign a new value to x1, because undefined is not truthy.
// Also note: getNewValue() is never executed.
x1 &&= getNewValue();
console.log(x1) // undefined

// Assigns a new value to x2, because a string is truthy.
x2 &&= 'b';
console.log(x1) // "b"
  • Logical or assignment (||=): Only assign a value when it was “falsy” before (false or converts to false).
let x1 = undefined;
let x2 = 'a';
const getNewValue = () => 'b';

// Assigns the new value to x1, because undefined is falsy.
x1 ||= 'b';
console.log(x1) // "b"

// Does not assign a new value to x2, because a string is not falsy.
// Also note: getNewValue() is never executed.
x2 ||= getNewValue();
console.log(x1) // "a"
  • WeakRef: Hold a “weak” reference to an object, without preventing the object from being garbage-collected.
const ref = new WeakRef(element);

// Get the value, if the object/element still exists and was not garbage-collected.
const value = ref.deref;
console.log(value); // undefined
// Looks like the object does not exist anymore.
  • Numeric literal separators (_): Separate numbers using for better readability. This does not affect functionality._
const int = 1_000_000_000;
const float = 1_000_000_000.999_999_999;
const max = 9_223_372_036_854_775_807n;
const binary = 0b1011_0101_0101;
const octal = 0o1234_5670;
const hex = 0xD0_E0_F0;

ES2022

  • Top level await: The keyword can now be used at the top level of ES modules, eliminating the need for a wrapper function and improving error handling.await
async function asyncFuncSuccess() {
  return 'test';
}
async function asyncFuncFail() {
  throw new Error('Test');
}
// PREVIOUSLY:
// Whenever we want to await a promise, this is only possible inside async functions.
// await asyncFuncSuccess(); // SyntaxError: await is only valid in async functions
// So we had to wrap it inside one, thereby losing error handling and top-level concurrency.
try {
  (async () => {
    console.log(await asyncFuncSuccess()); // "test"
    try {
      await asyncFuncFail();
    } catch (e) {
      // This is needed because otherwise the errors are never caught (or way too late without a proper trace).
      console.error(e); // Error: "Test"
      throw e;
    }
  })();
} catch (e) {
  // This is never triggered (or way too late without a proper trace) because the function is async.
  console.error(e);
}
// This is logged before the promise result because the async function is not awaited (because it couldn't).
console.log('Hey'); // "Hey"
// NEW:
// If the file is an ES module (set in package.json, has exports, named ".mts") we can just await at the top level instead.
console.log(await asyncFuncSuccess()); // "test"
try {
  await asyncFuncFail();
} catch (e) {
  console.error(e); // Error: "Test"
}
// This is logged after the promise result because all async calls are awaited.
console.log('Hello'); // "Hello"
  • #private: Make class members (properties and methods) private by naming them starting with . These then can only be accessed from the class itself. They can not be deleted or dynamically assigned. Any incorrect behavior will result in a JavaScript (not TypeScript) syntax error. This is not recommended for TypeScript projects, instead just use the existing keyword.#private
class ClassWithPrivateField {
  #privateField;
  #anotherPrivateField = 4;

  constructor() {
    this.#privateField = 42; // Valid
    this.#privateField; // Syntax error
    this.#undeclaredField = 444; // Syntax error
    console.log(this.#anotherPrivateField); // 4
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
  • static class members: Mark any class fields (properties and methods) as static.
class Logger {
  static id = 'Logger1';
  static type = 'GenericLogger';
  static log(message: string | Error) {
    console.log(message);
  }
}

class ErrorLogger extends Logger {
  static type = 'ErrorLogger';
  static qualifiedType;
  static log(e: Error) {
    return super.log(e.toString());
  }
}

console.log(Logger.type); // "GenericLogger"
Logger.log('Test'); // "Test"

// The instantiation of static-only classes is useless and only done here for demonstration purposes.
const log = new Logger();

ErrorLogger.log(new Error('Test')); // Error: "Test" (not affected by instantiation of the parent)
console.log(ErrorLogger.type); // "ErrorLogger"
console.log(ErrorLogger.qualifiedType); // undefined
console.log(ErrorLogger.id); // "Logger1"

// This throws because log() is not an instance method but a static method.
console.log(log.log()); // log.log is not a function
  • static initialization blocks in classes: Block which is run when a class is initialized, basically the “constructor” for static members.
class Test {
  static staticProperty1 = 'Property 1';
  static staticProperty2;
  static {
    this.staticProperty2 = 'Property 2';
  }
}

console.log(Test.staticProperty1); // "Property 1"
console.log(Test.staticProperty2); // "Property 2"
  • Import Assertions (non-standard, implemented in V8): Assert which type an import is using . Can be used to directly import JSON without having to parse it.import ... from ... assert { type: 'json' }
import json from './foo.json' assert { type: 'json' };
console.log(json.answer); // 42
  • RegExp match indices: Get the start and end indexes of regular expression matches and capture groups. This works for , and .RegExp.exec()String.match()String.matchAll()
const matchObj = /(test+)(hello+)/d.exec('start-testesthello-stop');

// PREVIOUSLY:
console.log(matchObj?.index);

// NEW:
if (matchObj) {
  // Start and end index of entire match (before we only had the start).
  console.log(matchObj.indices[0]); // [9, 18]

  // Start and end indexes of capture groups.
  console.log(matchObj.indices[1]); // [9, 13]
  console.log(matchObj.indices[2]); // [13, 18]
}
  • Negative indexing (.at(-1)): When indexing an array or a string, can be used to index from the end. It’s equivalent to for getting a value (but not setting).at()arr[arr.length — 1])
console.log([4, 5].at(-1)) // 5

const array = [4, 5];
array.at(-1) = 3; // SyntaxError: Assigning to rvalue
  • hasOwn: Recommended new way to find out which properties an object has instead of using . It works better for some edge cases.obj.hasOwnProperty()
const obj = { name: 'test' };

console.log(Object.hasOwn(obj, 'name')); // true
console.log(Object.hasOwn(obj, 'gender')); // false
  • Error cause: An optional cause can now be specified for Errors, which allows specifying of the original error when re-throwing it.
try {
  try {
    connectToDatabase();
  } catch (err) {
    throw new Error('Connecting to database failed.', { cause: err });
  }
} catch (err) {
  console.log(err.cause); // ReferenceError: connectToDatabase is not defined
}

Future (can already be used with TypeScript 4.9)

  • Auto-Accessor: Automatically make a property private and create get/set accessors for it.
class Person {
  accessor name: string;

  constructor(name: string) {
    this.name = name;
    console.log(this.name) // 'test'
  }
}

const person = new Person('test');

TypeScript

Basics (Context for further introductions)

  • Generics: Pass through types to other types. This allows for types to be generalized but still typesafe. Always prefer this over using or .anyunknown
// WITHOUT:
function getFirstUnsafe(list: any[]): any {
  return list[0];
}

const firstUnsafe = getFirstUnsafe(['test']); // typed as any

// WITH:
function getFirst<Type>(list: Type[]): Type {
  return list[0];
}

const first = getFirst<string>(['test']); // typed as string

// In this case the parameter can even be dropped because it is inferred from the argument.
const firstInferred = getFirst(['test']); // typed as string

// The types accepted as generics can also be limited using `extends`. The Type is also usually shortened to T.
class List<T extends string | number> {
  private list: T[] = [];

  get(key: number): T {
    return this.list[key];
  }

  push(value: T): void {
    this.list.push(value);
  }
}

const list = new List<string>();
list.push(9); // Type error: Argument of type 'number' is not assignable to parameter of type 'string'.
const booleanList = new List<boolean>(); // Type error: Type 'boolean' does not satisfy the constraint 'string | number'.

Past (Still relevant older introductions)

  • Utility Types: TypeScript contains many utility types, some of the most useful are explained here.
interface Test {
  name: string;
  age: number;
}

// The Partial utility type makes all properties optional.
type TestPartial = Partial<Test>; // typed as { name?: string | undefined; age?: number | undefined; }
// The Required utility type does the opposite.
type TestRequired = Required<TestPartial>; // typed as { name: string; age: number; }
// The Readonly utility type makes all properties readonly.
type TestReadonly = Readonly<Test>; // typed as { readonly name: string; readonly age: string }
// The Record utility type allows the simple definition of objects/maps/dictionaries. It is preferred to index signatures whenever possible.
const config: Record<string, boolean> = { option: false, anotherOption: true };
// The Pick utility type gets only the specified properties.
type TestLess = Pick<Test, 'name'>; // typed as { name: string; }
type TestBoth = Pick<Test, 'name' | 'age'>; // typed as { name: string; age: string; }
// The Omit utility type ignores the specified properties.type
type TestFewer = Omit<Test, 'name'>; // typed as { age: string; }
type TestNone = Omit<Test, 'name' | 'age'>; // typed as {}
// The Parameters utility type gets the parameters of a function type.
function doSmth(value: string, anotherValue: number): string {
  return 'test';
}
type Params = Parameters<typeof doSmth>; // typed as [value: string, anotherValue: number]
// The ReturnType utility type gets the return type of a function type.
type Return = ReturnType<typeof doSmth>; // typed as string

// There are many more, some of which are introduced further down.
  • Conditional Types: Conditionally set a type based on if some type matches / extends another type. They can be read in the same way as the conditional (ternary) operator in JavaScript.
// Only extracts the array type if it is an array, otherwise returns the same type.
type Flatten<T> = T extends any[] ? T[number] : T;

// Extracts out the element type.
type Str = Flatten<string[]>; // typed as string

// Leaves the type alone.
type Num = Flatten<number>; // typed as number
  • Inferring with conditional types: Not all generic types need to be specified by the consumer, some can also be inferred from the code. To have conditional logic based on inferred types, the keyword is needed. It in a way defines temporary inferred type variables.infer
// Starting with the previous example, this can be written more cleanly.
type FlattenOld<T> = T extends any[] ? T[number] : T;

// Instead of indexing the array, we can just infer the Item type from the array.
type Flatten<T> = T extends (infer Item)[] ? Item : T;

// If we wanted to write a type that gets the return type of a function and otherwise is undefined, we could also infer that.
type GetReturnType<Type> = Type extends (...args: any[]) => infer Return ? Return : undefined;

type Num = GetReturnType<() => number>; // typed as number

type Str = GetReturnType<(x: string) => string>; // typed as string

type Bools = GetReturnType<(a: boolean, b: boolean) => void>; // typed as undefined
  • Tuple Optional Elements and Rest: Declare optional elements in tuples using and the rest based on another type using .?...
// If we don't yet know how long a tuple is going to be, but it's at least one, we can specify optional types using `?`.
const list: [number, number?, boolean?] = [];
list[0] // typed as number
list[1] // typed as number | undefined
list[2] // typed as boolean | undefined
list[3] // Type error: Tuple type '[number, (number | undefined)?, (boolean | undefined)?]' of length '3' has no element at index '3'.

// We could also base the tuple on an existing type.
// If we want to pad an array at the start, we could do that using the rest operator `...`.
function padStart<T extends any[]>(arr: T, pad: string): [string, ...T] {
  return [pad, ...arr];
}

const padded = padStart([1, 2], 'test'); // typed as [string, number, number]
  • abstract Classes and methods: Classes and the methods within them can be declared as to prevent them from being instantiated.abstract
abstract class Animal {
  abstract makeSound(): void;

  move(): void {
    console.log('roaming the earth...');
  }
}

// Abstract methods need to be implemented when extended.
class Cat extends Animal {} // Compile error: Non-abstract class 'Cat' does not implement inherited abstract member 'makeSound' from class 'Animal'.

class Dog extends Animal {
  makeSound() {
    console.log('woof');
  }
}

// Abstract classes cannot be instantiated (like Interfaces), and abstract methods cannot be called.
new Animal(); // Compile error: Cannot create an instance of an abstract class.

const dog = new Dog().makeSound(); // "woof"
  • Constructor signatures: Define the typing of constructors outside of Class declarations. Should not be used in most cases, abstract classes can be used instead.
interface MyInterface {
  name: string;
}

interface ConstructsMyInterface {
  new(name: string): MyInterface;
}

class Test implements MyInterface {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

class AnotherTest {
  age: number;
}

function makeObj(n: ConstructsMyInterface) {
    return new n('hello!');
}

const obj = makeObj(Test); // typed as Test
const anotherObj = makeObj(AnotherTest); // Type error: Argument of type 'typeof AnotherTest' is not assignable to parameter of type 'ConstructsMyInterface'.
  • ConstructorParameters Utility Type: TypeScript helper function which gets the constructor parameters from a constructor type (but not a class).
// What if we wanted to get the constructor argument for our makeObj function.
interface MyInterface {
  name: string;
}

interface ConstructsMyInterface {
  new(name: string): MyInterface;
}

class Test implements MyInterface {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

function makeObj(test: ConstructsMyInterface, ...args: ConstructorParameters<ConstructsMyInterface>) {
  return new test(...args);
}

makeObj(Test); // Type error: Expected 2 arguments, but got 1.
const obj = makeObj(Test, 'test'); // typed as Test

TypeScript 4.0

  • Variadic Tuple Types: Rest elements in tuples can now be generic. The use of multiple rest elements is now also allowed.
// What if we had a function that combines two tuples of undefined length and types? How can we define the return type?

// PREVIOUSLY:
// We could write some overloads.
declare function concat(arr1: [], arr2: []): [];
declare function concat<A>(arr1: [A], arr2: []): [A];
declare function concat<A, B>(arr1: [A], arr2: [B]): [A, B];
declare function concat<A, B, C>(arr1: [A], arr2: [B, C]): [A, B, C];
declare function concat<A, B, C, D>(arr1: [A], arr2: [B, C, D]): [A, B, C, D];
declare function concat<A, B>(arr1: [A, B], arr2: []): [A, B];
declare function concat<A, B, C>(arr1: [A, B], arr2: [C]): [A, B, C];
declare function concat<A, B, C, D>(arr1: [A, B], arr2: [C, D]): [A, B, C, D];
declare function concat<A, B, C, D, E>(arr1: [A, B], arr2: [C, D, E]): [A, B, C, D, E];
declare function concat<A, B, C>(arr1: [A, B, C], arr2: []): [A, B, C];
declare function concat<A, B, C, D>(arr1: [A, B, C], arr2: [D]): [A, B, C, D];
declare function concat<A, B, C, D, E>(arr1: [A, B, C], arr2: [D, E]): [A, B, C, D, E];
declare function concat<A, B, C, D, E, F>(arr1: [A, B, C], arr2: [D, E, F]): [A, B, C, D, E, F];
// Even just for three items each, this is really suboptimal.

// Instead we could combine the types.
declare function concatBetter<T, U>(arr1: T[], arr2: U[]): (T | U)[];
// But this types to (T | U)[]

// NEW:
// With variadic tuple types, we can define it easily and keep the information about the length.
declare function concatNew<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U];

const tuple = concatNew([23, 'hey', false] as [number, string, boolean], [5, 99, 20] as [number, number, number]);
console.log(tuple[0]); // 23
const element: number = tuple[1]; // Type error: Type 'string' is not assignable to type 'number'.
console.log(tuple[6]); // Type error: Tuple type '[23, "hey", false, 5, 99, 20]' of length '6' has no element at index '6'.
  • Labeled Tuple Elements: Tuple elements can now be named like . If one of the elements is named, all of them must be named.[start: number, end: number]
type Foo = [first: number, second?: string, ...rest: any[]];

// This allows the arguments to be named correctly here, it also shows up in the editor.
declare function someFunc(...args: Foo);
  • Class Property Inference from Constructors: When a property is set in the constructor, the type can now be inferred and no longer needs to be set manually.
class Animal {
  // No need to set types when they are assigned in the constructor.
  name;

  constructor(name: string) {
    this.name = name;
    console.log(this.name); // typed as string
  }
}
  • JSDoc @deprecated Support: The JSDoc/TSDoc tag is now recognized by TypeScript.@deprecated
/** @deprecated message */
type Test = string;

const test: Test = 'dfadsf'; // Type error: 'Test' is deprecated.

TypeScript 4.1

  • Template Literal Types: When defining literal types, types can be specified through templating like . This allows the construction of complex string types, for example when combining multiple string literals.${Type}
type VerticalDirection = 'top' | 'bottom';
type HorizontalDirection = 'left' | 'right';
type Direction = `${VerticalDirection} ${HorizontalDirection}`;

const dir1: Direction = 'top left';
const dir2: Direction = 'left'; // Type error: Type '"left"' is not assignable to type '"top left" | "top right" | "bottom left" | "bottom right"'.
const dir3: Direction = 'left top'; // Type error: Type '"left top"' is not assignable to type '"top left" | "top right" | "bottom left" | "bottom right"'.

// This can also be combined with generics and the new utility types.
declare function makeId<T extends string, U extends string>(first: T, second: U): `${Capitalize<T>}-${Lowercase<U>}`;
  • Key Remapping in Mapped Types: Retype mapped types while still using their values like .[K in keyof T as NewKeyType]: T[K]
// Let's say we wanted to reformat an object but prepend its IDs with an underscore.
const obj = { value1: 0, value2: 1, value3: 3 };
const newObj: { [Property in keyof typeof obj as `_${Property}`]: number }; // typed as { _value1: number; _value2: number; value3: number; }
  • Recursive Conditional Types: Use conditional types inside of its definition themselves. This allows for types that conditionally unpack an infinitely nested value.
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;

type P1 = Awaited<string>; // typed as string
type P2 = Awaited<Promise<string>>; // typed as string
type P3 = Awaited<Promise<Promise<string>>>; // typed as string
  • Editor support for JSDOC @see tag: The JSDoc/TSDoc tag is now supported in editors.@see variable/type/link
const originalValue = 1;
/**
  * Copy of another value
  * @see originalValue
  */
const value = originalValue;
  • tsc --: The option can be used for the TypeScript CLI to explain which files are part of the compilation and why. This can be useful for debugging. Warning: For large projects or complex setups this will generate a lot of output, instead use or something similar.explainFiles--explainFilestsc --explainFiles | less
tsc --explainFiles

<<output
../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es5.d.ts
  Library referenced via 'es5' from file '../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2015.d.ts'
  Library referenced via 'es5' from file '../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2015.d.ts'
../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2015.d.ts
  Library referenced via 'es2015' from file '../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2016.d.ts'
  Library referenced via 'es2015' from file '../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2016.d.ts'
../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2016.d.ts
  Library referenced via 'es2016' from file '../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2017.d.ts'
  Library referenced via 'es2016' from file '../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2017.d.ts'
...
output
  • Destructured Variables Can Be Explicitly Marked as Unused: When destructuring, an underscore can be used to mark a variable as unused. This prevents TypeScript from throwing an “unused variable” error.
const [_first, second] = [3, 5];
console.log(second);

// Or even shorter
const [_, value] = [3, 5];
console.log(value);

TypeScript 4.3

  • Separate Write Types on Properties: When defining set/get accessors, the write/set type can now be different than the read/get type. This allows for setters that accept multiple formats of the same value.
class Test {
  private _value: number;

  get value(): number {
    return this._value;
  }

  set value(value: number | string) {
    if (typeof value === 'number') {
      this._value = value;
      return;
    }
    this._value = parseInt(value, 10);
  }
}
  • override: Explicitly mark inherited class methods as overrides using , so when the parent class changes, TypeScript can notify you that the parent method no longer exists. This allows for safer complex inheritance patterns.override
class Parent {
  getName(): string {
    return 'name';
  }
}

class NewParent {
  getFirstName(): string {
    return 'name';
  }
}

class Test extends Parent {
  override getName(): string {
    return 'test';
  }
}

class NewTest extends NewParent {
  override getName(): string { // Type error: This member cannot have an 'override' modifier because it is not declared in the base class 'NewParent'.
    return 'test';
  }
}
  • 静态索引签名:在类上使用静态属性时,现在也可以使用 .static [propName: string]: string
以前:
类测试 {}

测试。测试 = '';类型错误:类型“类型测试”上不存在属性“test”。

 NEW:class NewTest {
 static [key: string]:
 string;
} 

新测试。测试 = '';
  • 编辑器对 JSDOC @link 标记的支持:现在支持 JSDoc/TSDoc 内联标记,并将在编辑器中显示和解析。{@link variable/type/link}
常量原始值 = 1;
/** * {@link originalValue} 的副本

 */
const value = originalValue;

打字稿 4.4

  • 精确可选属性类型 (--exactOptionalPropertyTypes):使用编译器标志(或 in )赋值,因为隐式允许的属性不再允许(例如)。而是需要像 .--exactOptionalPropertyTypestsconfig.jsonundefinedundefinedproperty?: stringundefinedproperty: string | undefined
类测试 {
 名称?: 字符串;
 年龄:数字 |未定义;
}

const test = new Test();
测试。名称 = 未定义;类型错误:类型“undefined”不能分配给带有“exactOptionalPropertyTypes: true”的类型“string”。请考虑将“未定义”添加到目标类型。
测试。年龄 = 未定义;
控制台。日志(测试。年龄);//定义

打字稿 4.5

  • 等待的类型和承诺改进:新的实用程序类型从无限嵌套的承诺中提取值类型(就像对值所做的那样)。这也改进了 的类型推断。Awaited<>awaitPromise.all()
假设我们想要一个通用的等待值。
我们可以为此使用 Awaited 实用程序类型(它的源代码是前面示例的一部分),
// 因此无限嵌套的 Promise 都解析为其值。
类型 P1 = 等待<字符串>;键入为字符串
类型 P2 = 等待<承诺<字符串>>;键入为字符串
类型 P3 = 等待<承诺<承诺<字符串>>>;键入为字符串
  • 导入名称上的类型修饰符:在普通(非)导入语句中,关键字可用于指示该值应仅用于类型编译(并且可以剥离)。import typetype
导入类型的最佳方法是使用“import type”关键字来防止它们在编译后实际导入。
从“./file”导入 { 某物 };
从“./文件”导入类型 { SomeType };
这需要同一个文件的两个导入语句。

// 新:
// 现在这可以合并到一个语句中。
从“./file”导入 { 某些内容,键入 SomeType };
  • const 断言:定义常量时,可用于将它们准确地键入为文本类型。这有很多用例,使准确的打字更容易。它也制造对象和数组,防止常量对象的突变。as constreadonly
以前:
const obj = { name: 'foo', value: 9, toggle: false };键入为 { name: string; value: number; toggle: boolean; }
可以分配任何值,因为它们通常是键入的。
obj.name = 'bar';

常量元组 = ['名称', 4, 真];键入为 (string | number | boolean)[]
// 长度和确切类型无法从类型中确定。可以在任何地方分配任何值。
元组[0] = 0;
元组[3] = 0;

// NEW:const objNew = { name: 'foo', value: 9, toggle: 
false } as const;键入为 { readonly name: “foo”; readonly value: 9; readonly toggle: false; }
不能分配任何值(因为它被定义为“foo”(并且也是只读的))。
objNew.名称 = “酒吧”;类型错误:无法分配给“name”,因为它是只读属性。

const tupleNew = ['name', 4, true] as const;键入为只读 [“name”, 4, true]
// 长度和确切类型现在已定义,无法分配任何内容(因为它被定义为文字(并且也是只读的))。
元组新[0] = 0;类型错误:无法分配给“0”,因为它是只读属性。
元组新[3] = 0;类型错误:类型“只读 [”name“, 4, true]”中的索引签名仅允许读取。
  • 类中方法的代码段完成:当类继承方法类型时,现在建议在编辑器中将它们作为代码段。
 

打字稿 4.6

  • 索引访问推理改进当直接使用键索引 Type 时,该类型现在在同一对象上时会更准确。另外,这只是一个很好的例子来展示现代TypeScript的可能性。
接口 允许类型 {
 '数字': 数字;
 “字符串”:字符串;
 “布尔值”:布尔值;
}

// 记录指定允许的类型中的种类和值类型。
type UnionRecord<AllowedKeys extensions keyof AllowedTypes> = { [Key in AllowedKeys]:{ 
 kind:
 Key;
 值:允许的类型[键];
 logValue: (value: AllowedTypes[Key]) => void;
}}
[允许的密钥];

函数 logValue 只接受记录的值。
function processRecord<Key extensions keyof AllowedTypes>(record: UnionRecord<Key>) {
 record.logValue(record.值);
}

processRecord({
 kind: 'string', value: 'hello!', // 用于隐式具有字符串 | 数字 | 布尔值类型的值,


 // 但现在被正确地推断为字符串。
 日志值:值 => {
 控制台。日志(值。到大写());
  }
});
  • TypeScript Trace Analyzer (--generateTrace):该选项可用于 TypeScript CLI 生成包含有关类型检查和编译过程的详细信息的文件。这有助于优化复杂类型。--generateTrace <Output folder>
tsc --generateTrace trace

 cat trace/trace.json
<<output[{“name”:“process_name”,“args”:{“name”:“tsc”},“cat”:“__metadata”,“ph”:“M”,“ts”:...,“pid”:1,“tid”:1},{“name”:“thread_name”,“args”:{“name”:“Main”},“cat”:“__metadata”,“ph”:“M”,“ts”:...,“pid”:1,“tid”:1},{“name”:“TracingStartedInBrowser”,“cat”:“disabled-by-default-devtools.timeline”,“ph”:“M”,“ts”:...,

“pid

“:1,”tid“:1},{”pid“:1,”tid“:1,”ph“:”B“,”cat“:”program“,”ts“:...,
”name“:”createProgram“,”args“:{”configFilePath“:”/...“,”rootDir“:”/...“}},{”pid“:1,”tid“:1,”ph“:”B“,”cat“:”parse“,”ts“:...,”name“:”createSourceFile“,”args“:{”path“:”/...“}},{”pid“:1,”tid“:1,”ph“:”E“,”cat“:”parse“,”ts“:...,

”name“:”createSourceFile“,”args“:{”path“:”/...“}},
{“pid”:1,“tid”:1,“ph”:“X”,“cat”:“program”,“ts”:...,“name”:“resolveModuleNamesWorker”,“dur”:...,“args”:{“containingFileName”:“/...”}},
...
 output cat trace/types.json
<<output

[{“id”:1,“intrinsicName”:“any”,“recursionId”:0,“flags”:[“...”]},{“id”:2,“intrinsicName”:“any”,“recursionId”:1,“flags”:[“...”]},{“id”:3,“intrinsicName”:“any”,“recursionId”:2,“flags”:[“...”]},{“id”:4,“intrinsicName”:“error”,“recursionId”:3,“flags”:[“...”]},{“id”:5,



“intrinsicName”:“未解析
“,”recursionId“:4,”flags“:[”...“]},{”id“:6,”intrinsicName“:”any“,”recursionId“:5,”flags“:[”...“]},{”id“:7,”intrinsicName“:”intrinsic“,”recursionId“:6,”flags“:[”...“]},{”id“:8,”intrinsicName“:”unknown“,”recursionId“:7,”flags“:[”...“]},{”id“:9,”intrinsicName“:”unknown“,”recursionId“:8,”flags“:[”...“]},{”id“:10,”intrinsicName“:”undefined“,”recursionId“:9,”flags“:[”...“]},





{“id”:11,“intrinsicName”:“undefined”,“recursionId”:10,“flags”:[“...”]},{“id”:12,“intrinsicName”:“null”,“recursionId”:11,“flags”:[“...”]},{“id”:13,“intrinsicName”:“string”,“recursionId”:12,“flags”:[“...”]},


...
输出

TypeScript 4.7

  • ECMAScript Module Support in Node.js: When using ES Modules instead of CommonJS, TypeScript now supports specifying the default. Specify it in the .tsconfig.json
...
"compilerOptions": [
  ...
  "module": "es2020"
]
...
  • 在 package.json 中键入:可以将 中的字段设置为 ,这是将 node.js 与 ES 模块一起使用所必需的。在大多数情况下,这对于 TypeScript 来说已经足够了,并且不需要上面的编译器选项。typepackage.json"module"
...
"type": "module"
...
  • 实例化表达式:实例化表达式允许在引用值时指定类型参数。这允许在不创建包装器的情况下缩小泛型类型。
类 列表<T> {
 私有列表: T[] = [];

 get(key: number): T {
 返回这个。列表[键];


} push(value: T): void {
 this.列表。推送(值);
 }}


function makeList<T>(items: T[]): List<T> {
 const list = new List<T>();
 items. 对于每个(项目 => 列表。推送(项目));
 返回列表;
}

// 假设我们想要一个函数来创建一个列表,但只允许某些值。
之前:
// 我们需要手动定义一个包装函数并传递参数。
function makeStringList(text: string[]) {
 return makeList(text);
}

// NEW:
// 使用实例化表达式,这要容易得多。
const makeNumberList = makeList<number>;
  • 扩展 推断类型变量的约束:在条件类型中推断类型变量时,现在可以使用 直接缩小/约束它们。extends
假设我们要键入一个类型,如果它是字符串,则仅获取数组的第一个元素。
我们可以为此使用条件类型。

以前:
类型 FirstIfStringOld<T> =
 T 扩展 [推断 S, ...未知[]]
 ?S 扩展字符串?S : 从不 : 从不
;

但这需要两个嵌套的条件类型。我们也可以合二为一。
类型 FirstIfString<T> =
 T 扩展 [string, ...未知[]]
 // 从“T”
中获取第一种类型?T[0]
 : 从不;

这仍然是次优的,因为我们需要为数组索引正确的类型。

新:
// 使用推断类型变量的扩展约束,可以更轻松地声明这一点。
类型 FirstIfStringNew<T> =
 T 扩展 [推断 S 扩展字符串,...未知[]]
 ?S
 : 从不;
请注意,键入之前的工作方式相同,这只是一种更干净的语法。

类型 A = FirstIfStringNew<[string, number, number]>;键入为字符串
类型 B = FirstIfStringNew<[“hello”, number, number]>;键入为 “hello”type C = FirstIfStringNew<[“hello”
 |“世界”,布尔值]>;输入为“你好” |“world”
类型 D = FirstIfStringNew<[boolean, number, string]>;键入为从不
  • 类型参数的可选方差注释:泛型在检查它们是否“匹配”时可以有不同的行为,例如,对于 getter 和 setter,允许继承是相反的。为清楚起见,现在可以选择指定这一点。
假设我们有一个接口/一个扩展另一个接口的类。
接口  动物 {
 动物的东西: 任何;
}

接口  狗扩展动物 {
 狗的东西: 任何;
}

我们有一些通用的“getter”和“setter”。
类型吸气<T> = () => T;

类型二传手<T> = (值: T) => 空;

如果我们想找出 Getter<T1> 是否与 Getter<T2> 或 Setter<T1> 匹配 Setter<T2>,这取决于协方差。
function useAnimalGetter(getter: Getter<Animal>) {
 getter();
}

现在我们可以将 Getter 传递到函数中。
useAnimalGetter((() => ({ animalStuff: 0 }) as Animal));
这显然有效。

但是,如果我们想使用返回狗的 Getter 怎么办?
useAnimalGetter((() => ({ animalStuff: 0, dogStuff: 0 }) as Dog)  );
这也有效,因为狗也是动物。

function useDogGetter(getter: Getter<Dog>) {
 getter();
}

如果我们对useDogGetter函数尝试相同的操作,我们将不会得到相同的行为。
useDogGetter((() => ({ animalStuff: 0 }) as Animal));类型错误:属性“dogStuff”在类型“动物”中缺失,但在类型“狗”中是必需的。
这不起作用,因为需要狗,而不仅仅是动物。

useDogGetter((() => ({ animalStuff: 0, dogStuff: 0 }) as Dog)  );
但是,这有效。

直觉上,我们可能期望二传者的行为相同,但他们没有。
function setAnimalSetter(setter: Setter<Animal>, value: Animal) {
 setter(value);
}

如果我们传递相同类型的 Setter,它仍然有效。
setAnimalSetter((value: Animal) => {}, { animalStuff: 0 });

function setDogSetter(setter: Setter<Dog>, value: Dog) {
 setter(value);
}

这里也一样。
setDogSetter((value: Dog) => {}, {动物的东西: 0, 狗的东西: 0 }  );

但是,如果我们将 Dog Setter 传递到 setAnimalSetter 函数中,则行为与 Getter 相反。
setAnimalSetter((value: Dog) => {}, { animalStuff: 0, dogStuff: 0 }); 类型错误:类型“(值:狗)=> void”的参数不能分配给类型为“Setter<Animal>”的参数。

这一次它以相反的方式工作。
setDogSetter((value: Animal) => {}, { animalStuff: 0, dogStuff: 0 }); 


要向 TypeScript 发出此信号(不需要,但有助于提高可读性),请使用新的类型参数可选方差注释。
类型 GetterNew<out T> = () => T;
类型二传手新<在 T> = (值: T) => 空;
  • 分辨率自定义:使用具有自定义文件后缀的环境(例如,对于本机应用程序版本)时,现在可以为 TypeScript 指定这些后缀以正确解析导入。在 中指定它们。moduleSuffixes.iostsconfig.json
...
“编译器选项”: [
 ...
 “模块后缀”: [“.ios”, “.native”, “”]
]
...
从“./foo”导入 * 作为 foo;
首先检查 ./foo.ios.ts、./foo.native.ts,最后检查 ./foo.ts。
  • 在编辑器中转到源定义:在编辑器中,可以使用新的“转到源定义”菜单选项。它类似于“转到定义”,但首选和文件而不是类型定义()。.ts.js.d.ts
   

打字稿 4.9

  • 满足运算符:运算符允许检查与类型的兼容性,而无需实际分配该类型。这允许在保持兼容性的同时保持更准确的推断类型。satisfies
假设我们有一个对象/地图/字典,它存储各种项目及其颜色。
const obj = { fireTruck: [255, 0, 0], bush: '#00ff00', ocean: [0, 0,

 255]} // 键入为 {
 fireTruck: number[]; bush: string; ocean: number[]
; }

这隐式类型化了属性,因此我们可以对数组和字符串进行操作。
const rgb1 = obj.fireTruck[0];键入为数字
常量十六进制 = obj.bush;键入为字符串

// 假设我们只想允许某些对象。
我们可以使用 Record 类型。
常量旧对象: 记录<字符串, [数字, 数字, 数字] | string> = {
 fireTruck: [255, 0, 0], bush: '#00ff00', ocean: [0, 0, 255]} // 键入为 Record<string, [number, number,

 number]
 | string>
// 但是现在我们失去了属性的类型。
const oldRgb1 = oldObj.消防车[0];键入为字符串 | 数字
常量 oldHex = oldObj。灌木丛;键入为字符串 | 数字

// 新:
// 使用 satifies 关键字,我们可以检查与类型的兼容性,而无需实际分配它。
const newObj = {
 fireTruck: [255, 0, 0], bush: '#00ff00', ocean: [0, 0, 255]} 满足记录<字符串,

 [数字, 数字, 数字] | 
string> // 键入为 { fireTruck: [number, number, number]; bush: string; ocean: [number, number, number]; }
我们仍然有属性的类型,数组甚至通过成为元组变得更加准确。
const newRgb1 = newObj.消防车[0];键入为 number
const newRgb4 = newObj。消防车[3];类型错误:长度为“3”的元组类型“[数字,数字,数字]”在索引“3”处没有元素。
const newHex = newObj.灌木丛;键入为字符串
  • 编辑器的“删除未使用的导入”和“排序导入”命令:在编辑器中,新命令(和自动修复)“删除未使用的导入”和“排序导入”使管理导入变得更加容易。
 

标签:JavaScript,网站开发,编程
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有