ICode9

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

过去 3 年的所有 JavaScript 和 TypeScript 特性

2023-03-12 12:20:14  阅读:700  来源: 互联网

标签:JavaScript  编程 Web开发


概述

  • JavaScript / ECMAScript(最早的在前)
  • 打字稿(最旧的在前)

ECMA脚本

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

  • 标记的模板文字:通过在模板文字前面添加一个函数名,该函数将传递模板文字的部分和模板值。这有一些有趣的用途。
// 假设我们要编写一种方法来记录包含数字的任意字符串,但要格式化数字。
// 我们可以为此使用标记模板。
function  formatNumbers ( strings: TemplateStringsArray, number : number ): string { 
  return strings[ 0 ] + number . toFixed ( 2 ) + 字符串[ 1 ]; 
}
控制台log (formatNumbers `这是值:${ 0 },这很重要。` ); // 这是值:0.00,很重要。

// 或者如果我们想“翻译”(在这里更改为小写)字符串中的翻译键。
function  translateKey ( key: string ): string {
  返回键。toLocaleLowerCase (); 
} 
function  translate ( strings: TemplateStringsArray, ...expressions: string [] ): string {
  返回字符串。reduce ( ( accumulator, currentValue, index ) => accumulator + currentValue + translateKey (expressions[index] ?? '' ), ''); 
}
控制台日志(将`你好,这是${ 'NAME' }翻译成${ 'MESSAGE' }。`);// 你好,这是消息的名称。
  • 符号:对象的唯一键:Symbol("foo") === Symbol("foo"); // false内部使用。
const  obj : { [ index : string ]: string } = {}; 

const symbolA =符号( 'a' ); 
常量符号 B =符号对于'b'); 

控制台日志(symbolA.description // "a"

 obj[symbolA] = 'a' ; 
obj[symbolB] = 'b' ; 
obj[ 'c' ] = 'c' ; 
对象。d = 'd' ; 

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

// 当使用 for ... in. 
for ( const i in obj) { 
  console . 日志(一);// "c", "d"
 }

ES2020

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

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

// 新:
// 相反,我们可以使用可选链接。
const  objectNew : {名称:字符串} | 未定义=数学随机() > 0.5 ? 不明确的:{名称'测试' }; 
const valueNew = objectNew?。姓名

// 这也可以用于索引和函数。
常量 数组字符串[] | 未定义=数学随机() > 0.5 ? 未定义:[ '测试' ];
const item = array?.[ 0 ]; 
常量 函数:(()=> 字符串)| 未定义=数学随机() > 0.5 ?未定义: () =>  '测试' ; 
const result = func?.();
  • Nullish coalescing operator (??) :可以使用||new运算符代替有条件赋值的运算符。??它不是应用于所有虚假值,而是仅应用于undefinednull
 量值字符串| 未定义=数学随机() > 0.5 ? 未定义“测试”

// PREVIOUSLY: 
// 当我们想在值未定义或 null 时有条件地分配其他内容时,我们可以使用“||” 操作员。
const anotherValue = 值 || '你好' ; 
控制台日志(另一个值);// "test" 或 "hello" 

// 这在使用真值时工作正常,但如果我们要与 0 或空字符串进行比较,它也适用。
const incorrectValue = ''|| “不正确”
控制台日志(不正确的值);// 总是“不正确” 
const anotherIncorrectValue = 0 || “不正确”
控制台日志(另一个不正确的值);// 总是“不正确” 

// 新:
// 相反,我们可以使用新的无效合并运算符。它仅适用于未定义和空值。
const newValue = 值 ?? '你好' ; 
控制台log (newValue) // 始终为“hello” 

// 现在不替换虚假值。
??“不正确”
控制台日志(正确值);// 总是 "" 
const anotherCorrectValue = 0 ?? “不正确”
控制台日志(另一个正确值);// 总是 0
  • import():动态导入,就像import ... from ...,但在运行时和使用变量。
导入模块;
if (shouldImport) { 
  importModule = await  import ( './module.mjs' ); 
}
  • String.matchAll:在不使用循环的情况下获取正则表达式的多个匹配项,包括它们的捕获组。
const stringVar = 'testhello,testagain,' ; 

// PREVIOUSLY: 
// 只获取匹配项,但不获取它们的捕获组。
控制台log (stringVar.match ( /test([\w]+?),/g ) ); // ["testhello,", "testagain,"] // 只得到一个匹配项,包括它的捕获组。const singleMatch = 字符串变量。匹配( /test([\w]+?),/ ); 如果(单一匹配){控制台日志(单匹配[ 0 ]);// “testhello”,控制台日志(单匹配[ 1




  
  ]); // "hello"
 } 

// 得到相同的结果,但是非常不直观(exec 方法保存最后一个索引)。
// 需要定义在循环外(保存状态)并且是全局的(/g),
// 否则会产生死循环。
const regex = /test([\w]+?),/g ; 
让执行匹配;
while ((execMatch = regex.exec ( stringVar)) !== null ) { 
  console . 日志(execMatch[ 0 ]);// "testhello,", "testagain,"
  控制台日志(execMatch[ 1 ]);// “再一次问好”
} 

// 新:
// 正则表达式需要是全局的 (/g),否则也没有任何意义。
const matchesIterator = stringVar. matchAll ( /test([\w]+?),/g ); 
// 需要迭代或转换为数组(Array.from()),不能直接索引。
for ( const match of matchesIterator) { 
  console . 日志(匹配[ 0 ]);// "testhello,", "testagain,"
  控制台日志(匹配[ 1 ]);// “你好”,“再次”
 }
  • Promise.allSettled():喜欢Promise.all(),但等待所有承诺完成并且不会在第一次拒绝/抛出时返回。它使处理所有错误变得更加容易。
async  function  success1 ( ) { return  'a' } 
async  function  success2 ( ) { return  'b' } 
async  function  fail1 ( ) { throw  'fail 1' } 
async  function  fail2 ( ) { throw  'fail 2' } 

// 上一个:
控制台log ( await  Promise .all ([ success1 (), success2 ( ) ])); // ["a", "b"] // 但是:

尝试{
  等待 承诺所有([成功1(),成功2(),失败1(),失败2()]); 
} catch (e) {
  控制台. 记录(e); // "fail 1"
 } 
// 注意:我们只捕获一个错误,无法访问成功值。

// PREVIOUS FIX(真的不是最理想的):
控制台log ( await  Promise .all ([ // ["a", "b", undefined, undefined] success1 ( ). catch ( e =>
  {控制台记录(e); }),
  成功2()。catch ( e => { console.log (e); }), fail1 
  ( ) . catch ( e => { console .log (e); }), // “失败 1” fail2 () catch ( e => { console.log (e); } )])) ; // "fail 2" // 新:const results = await Promise . 全部解决([
  


 success1 (), success2 (), fail1 (), fail2 ()]); 
const成功结果 = 结果
过滤器结果=>结果。状态=== '已完成'
map ( result => (result as  PromiseFulfilledResult < string >). value ); 
控制台日志(成功结果);// ["a", "b"]
结果。过滤器结果=>结果。状态=== '拒绝')。forEach ( error => { 
  console . log ((error as  PromiseRejectedResult ). reason ); // "fail 1", "fail 2"
 }); 
// 或:
for ( const result of results) { 
  if (result.status === ' fulfilled' ) { 
    console . 日志(结果。);// "a", "b"
   } else  if ( result.status ==='拒绝' ) {
    控制台. 日志(结果。原因);// “失败 1”,“失败 2”
   } 
}
  • BigInt:新的BigInt数据类型允许准确地存储和操作大(整数)数字,这可以防止 JavaScript 将数字存储为浮点数而产生的错误。它们可以使用BigInt()构造函数构造(最好使用字符串以防止不准确)或通过附加n在数字末尾来构造。
// 以前:
// JavaScript 将数字存储为浮点数,因此总会有一些不准确
// 但更重要的是,在某个数字之后的整数运算开始出现不准确。
const maxSafeInteger = 9007199254740991 ; 
控制台日志(maxSafeInteger === Number。MAX_SAFE_INTEGER // true // 如果我们比较比它大的数字,可能会不准确。控制台日志数字。MAX_SAFE_INTEGER + 1 ===数字。MAX_SAFE_INTEGER + 2 _ _


); 

// 新:
// 使用新的 BigInt 数据类型,理论上我们可以存储和操作无限大(整数)的数字。
// 通过使用 BigInt 构造函数或在数字末尾附加“n”来使用它。
const maxSafeIntegerPreviously = 9007199254740991n ; 
控制台日志(maxSafeIntegerPreviously);// 9007199254740991 

const anotherWay = BigInt ( 9007199254740991 ); 
控制台日志(另一种方式);// 9007199254740991 

// 如果我们使用数字构造函数,我们不能安全地传递大于 MAX_SAFE_INTEGER 的整数。
常数不正确 = BigInt ( 9007199254740992 ); 
控制台日志(不正确);// 9007199254740992 
const incorrectAgain = BigInt ( 9007199254740993 ); 
控制台日志(再次不正确);// 9007199254740992 
// 糟糕,它们转换为相同的值。

// 而是使用字符串或更好的其他语法。
const correct = BigInt ( '9007199254740993' ); 
控制台日志(正确);// 9007199254740993 
const correctAgain =9007199254740993n ; 
控制台日志(再次正确);// 9007199254740993 

// 十六进制、八进制和二进制数也可以作为字符串传递。
const hex = BigInt ( '0x1ffffffffffffff' ); 
控制台日志(十六进制);// 9007199254740991 
const octal = BigInt ( '0o377777777777777777' ); 
控制台日志(八进制);// 9007199254740991 
const binary = BigInt ( '0b1111111111111111111111111111111111111111111111111111' ); 
安慰. 日志(二进制);// 9007199254740991 

// 大多数算术运算的工作方式与您预期的一样,
// 尽管另一个运算符也需要是 BigInt。所有操作也返回 BigInts。
常量加法 = maxSafeIntegerPreviously + 2n ; 
控制台日志(添加);// 9007199254740993 

const multiplication = maxSafeIntegerPreviously * 2n ; 
控制台日志(乘法);// 18014398509481982 

const减法 = 乘法 - 10n ; 
控制台日志(减法);// 18014398509481972 

const模 = 乘法 % 10n ; 
控制台日志(模数);// 2 

const求幂 = 2n ** 54n ; 
控制台日志(求幂);// 18014398509481984 

const exponentiationAgain = 2n ^ 54n ; 
控制台日志(再次取幂);// 18014398509481984 

const negative = exponentiation * - 1n ; 
控制台日志(消极的); // -18014398509481984 

// 除法的工作方式有点不同,因为 BigInt 只能存储整数。
const除法 = 乘法 / 2n ; 
控制台日志(除法);// 9007199254740991 
// 对于可整除的整数,这很好用。

// 但对于不可整除的数字,这将类似于整数除法(向下舍入)。
const divisionAgain = 5n / 2n ; 
控制台日志(再次划分);// 2 

// 非 BigInt 数字没有严格相等(但松散相等)。
控制台日志( 0n === 0 ); // 假
控制台日志0n == 0);// true 

// 但是比较按预期工作。
控制台对数( 1n < 2 ); // 真正的
控制台对数( 2n > 1 ); // 真正的
控制台对数( 2 > 2 ); // 假
控制台对数( 2n > 2); // 假
控制台日志( 2n >= 2 ); // true 

// 它们属于“bigint”类型
console日志类型为 1n);// "bigint" 

// 它们可以转换回常规数字(有符号和无符号(无负数))。
// 虽然这当然会牺牲准确性。可以指定有效位数。

控制台日志BigInt.asIntN0  -2n // 0
控制台记录(大整数asIntN ( 1 , - 2n )); // 0
控制台日志BigInt.asIntN2-2n // -2 // 通常你会使用更多的位数。// 负数在转换为无符号数时会被转换为2的补码。控制台日志BigInt.asUintN8  -2n // 254



  • globalThis:在全局上下文中访问变量,无论环境如何(浏览器、Node.js 等)。仍然被认为是不好的做法,但有时是必要的。类似于this浏览器的顶层。
控制台日志(globalThis。数学);// 数学对象
  • import.meta: 使用 ES-modules 时,获取当前模块 URL import.meta.url
控制台日志导入。url // “文件://...”
  • export * as … from …:轻松将默认值重新导出为子模块。
 另一个模块”导出*
 “模块”导入{am}

ES2021

  • String.replaceAll():替换字符串中子字符串的所有实例,而不是始终使用带有全局标志 (/g) 的正则表达式。
const testString = 'hello/greeting everybody/everybody' ; 
// PREVIOUSLY: 
// 仅替换第一个实例
控制台日志(testString。替换'/''|')); // 'hello|greetings everyone/everybody' 

// 相反,需要使用正则表达式,这对性能来说更糟,需要转义。
// 不是全局标志 (/g)。
控制台日志(testString.replace ( / \//g , '|' )); // '你好|大家好|大家' 

// 新:
// 使用 replaceAll 这会更清晰、更快速。
控制台日志(testString.replaceAll '/'  ' |')); // '你好|大家好|大家'
  • Promise.any:当只需要一个承诺列表的一个结果时,它返回第一个结果,它只在所有承诺都拒绝时拒绝并返回一个AggregateError, 而不是Promise.race,它会立即拒绝。
async  function  success1 ( ) { return  'a' } 
async  function  success2 ( ) { return  'b' } 
async  function  fail1 ( ) { throw  'fail 1' } 
async  function  fail2 ( ) { throw  'fail 2' } 

// 上一个:
控制台log ( await  Promise . race ([ success1 (), success2 ()])); // "a" 
// 但是:
尝试{
  等待 承诺race ([ fail1 (), fail2 (), success1 (), success2 ()]); 
} catch (e) {
  控制台. 记录(e); // "fail 1"
 } 
// 注意:我们只捕获一个错误,无法访问成功值。

// PREVIOUS FIX(真的不是最理想的):
控制台log ( await  Promise . race ([ // "a" 
  fail1 (). catch ( e => {控制台. 记录(e); }), // “失败 1” 
  fail2 (). catch ( e => { console .log (e); }), // “fail 2” 
  success1 (). catch ( e => { console.log (e); }), success2 ( ) . catch ( e => { console.log (e); } )])) ; // 新:控制台日志等待承诺任何([失败1
  


 (), fail2 (), success1 (), success2 ()])); // "a" 
// 并且它仅在所有承诺都拒绝时拒绝并返回包含所有错误的 AggregateError。
尝试{
  等待 承诺任何([ fail1 (), fail2 ()]); 
} catch (e) {
  控制台. 记录(e); // [AggregateError: 所有承诺都被拒绝] 
  console . 日志(例如错误);// ["失败 1", "失败 2"]
 }
  • Nullish coalescing assignment (??=):仅在之前为“nullish”(null 或 undefined)时才分配一个值。
x1 =未定义
x2 = 'a' ; 
const  getNewValue = ( ) => 'b' ; 

// 将新值赋给 x1,因为 undefined 是无效的。
x1 ??= 'b' ; 
控制台log (x1) // "b" 

// 不给 x2 赋新值,因为字符串不是空值。
// 另请注意:getNewValue() 永远不会执行。
x2 ??= getNewValue (); 
控制台日志(x1)//“a”
  • 逻辑与赋值(&&=):只有在之前为“真”时才赋值(真或转换为真的值)。
x1 =未定义
x2 = 'a' ; 
const  getNewValue = ( ) => 'b' ; 

// 不给 x1 赋新值,因为 undefined 不是 truthy。
// 另请注意:getNewValue() 永远不会执行。
x1 &&= getNewValue (); 
控制台log (x1) // undefined 

// 为 x2 分配一个新值,因为字符串是真实的。
x2 &&= 'b' ; 
控制台日志(x1)//“b”
  • 逻辑或赋值(||=):只有在之前为“falsy”时才赋值(false 或转换为false)。
x1 =未定义
x2 = 'a' ; 
const  getNewValue = ( ) => 'b' ; 

// 将新值赋给 x1,因为 undefined 是假的。
x1 ||= 'b' ; 
控制台log (x1) // "b" 

// 不给 x2 赋新值,因为字符串不是假的。
// 另请注意:getNewValue() 永远不会执行。
x2 ||= getNewValue (); 
控制台日志(x1)//“a”
  • WeakRef:保持对对象的“弱”引用,而不阻止对象被垃圾收集。
const ref = new  WeakRef (元素); 

// 如果对象/元素仍然存在并且没有被垃圾回收,则获取值。
量值 = 参考。取消引用
控制台日志(值);// undefined 
// 看起来这个对象已经不存在了。
  • 数字文字分隔符 (_):分隔数字_以提高可读性。这不会影响功能。
常数int = 1_000_000_000 ; 
const float = 1_000_000_000.999_999_999 ; 
const max = 9_223_372_036_854_775_807n ; 
常量二进制 = 0b1011_0101_0101 ; 
常量八进制 = 0o1234_5670 ; 
常量十六进制 = 0xD0_E0_F0 ;

ES2022

  • 顶层 awaitawait关键字现在可以在 ES 模块的顶层使用,消除了对包装函数的需要并改进了错误处理。
异步 函数 asyncFuncSuccess ( ) { 
  return  'test' ; 
} 
async  function  asyncFuncFail ( ) { 
  throw  new  Error ( 'Test' ); 
} 
// PREVIOUSLY: 
// 每当我们想要等待一个承诺时,这只能在异步函数中实现。
// 等待 asyncFuncSuccess(); // SyntaxError: await 仅在异步函数中有效
// 所以我们不得不将它包装在一个函数中,从而失去了错误处理和顶级并发性。
尝试{ 
  (异步() => {
    控制台日志(等待 asyncFuncSuccess ()); // “测试”
    尝试{ 
      await  asyncFuncFail (); 
    } catch (e) { 
      // 这是必需的,否则错误永远不会被捕获(或者在没有适当跟踪的情况下为时已晚)。
      控制台错误(e);// 错误:“测试”
      抛出e; 
    } 
  })(); 
} catch (e) { 
  // 这永远不会被触发(或者太晚没有适当的跟踪)因为函数是异步的。
  控制台错误(e);
}
// 这是在 promise 结果之前记录的,因为没有等待异步函数(因为它不能)。
控制台日志'嘿'); // "Hey" 
// NEW: 
// 如果文件是一个 ES 模块(在 package.json 中设置,有导出,名为“.mts”),我们可以在顶层等待。
控制台日志等待 asyncFuncSuccess());// “测试”
尝试{ 
  await  asyncFuncFail (); 
} catch (e) {
  控制台. 错误(e);// 错误:“测试”
 }
// 这是在承诺结果之后记录的,因为等待所有异步调用。
控制台日志'你好'); // “你好”
  • #private: 通过以#然后只能从类本身访问这些。它们不能被删除或动态分配。任何不正确的行为都会导致 JavaScript(而非 TypeScript)语法错误。对于 TypeScript 项目不推荐这样做,而是使用 existingprivate关键字。
 ClassWithPrivateField { 
  #privateField; 
  #anotherPrivateField = 4 ; 

  构造函数( ) {
    这个.#privateField = 42 ; // 有效
    . #privateField; // 语法错误
    this .#undeclaredField = 444 ; // 语法错误
    控制台. 日志这个。#anotherPrivateField);// 4
   } 
}

常量实例 = new  ClassWithPrivateField (); 
实例.#privateField === 42 ;// 语法错误
  • 静态类成员:将任何类字段(属性和方法)标记为静态。
 Logger { 
  static id = 'Logger1' ; 
  静态 类型= 'GenericLogger' ; 
  静态 日志消息:字符串|错误){
    控制台日志(消息);
  } 
} 

class  ErrorLogger  extends  Logger { 
  static  type = 'ErrorLogger' ; 
  静态限定类型;
  静态 日志e:错误){
    返回 超级log (e.toString ( )); 
  } 
}

控制台日志记录器类型);//“GenericLogger”
记录器日志'测试'); // "Test" 

// static-only 类的实例化是无用的,只是为了演示目的而在这里完成。
const log = new  Logger (); 

错误记录器日志 错误“测试”));// 错误:“测试”(不受父实例化的影响)
控制台日志错误记录器类型);// “ErrorLogger”
控制台. 日志ErrorLogger.qualifiedType _ // 未定义的
控制台日志ErrorLogger.id _ // "Logger1" 

// 这会抛出异常,因为 log() 不是实例方法而是静态方法。
控制台log (log.log ( )); // log.log 不是函数
  • 类中的静态初始化块:初始化类时运行的块,基本上是静态成员的“构造函数”。
 测试{ 
  static staticProperty1 = '属性 1' ; 
  静态静态属性2;
  静态{
    这个staticProperty2 = '属性 2' ; 
  } 
}

控制台日志测试。staticProperty1 //“属性 1”
控制台日志测试。staticProperty2 // "属性 2"
  • 导入断言(非标准,在 V8 中实现):断言导入使用的是哪种类型import ... from ... assert { type: 'json' }可用于直接导入 JSON 而无需解析它。
 './foo.json'导入json assert { type : 'json' }; 
控制台日志(json.answer // 42
  • RegExp match indices :获取正则表达式匹配和捕获组的开始和结束索引。这适用RegExp.exec()String.match()String.matchAll()
const matchObj = /(test+)(hello+)/ d. exec ( '开始-testesthello-停止' ); 

// 以前:
控制台日志(匹配对象?索引);

// 新:
if (matchObj) { 
  // 整个匹配的开始和结束索引(之前我们只有开始)。
  控制台log (matchObj.indices [ 0 ] ); // [9, 18] 

  // 捕获组的开始和结束索引。
  控制台log (matchObj.indices [ 1 ] ); // [9, 13]
  控制台. log (matchObj.indices [ 2 ] ); // [13, 18]
 }
  • 负索引(.at(-1)):索引数组或字符串时,at()可用于从末尾开始索引。它相当于arr[arr.length — 1])获取一个值(但不是设置)。
控制台log ([ 4 , 5 ].at ( - 1 )) // 5

常量数组 = [ 4 , 5 ]; 
大批。(- 1 ) = 3 处// 语法错误:分配给右值
  • hasOwn:推荐的新方法来找出对象具有哪些属性而不是使用obj.hasOwnProperty()它在某些边缘情况下效果更好。
const obj = {名称'测试' }; 

控制台log ( Object . hasOwn (obj, 'name' )); // 真正的
控制台log ( Object . hasOwn (obj, '性别' )); // 错误的
  • 错误原因:现在可以为错误指定可选原因,允许在重新抛出时指定原始错误。
尝试{
  尝试{ 
    connectToDatabase (); 
  } catch (err) { 
    throw  new  Error ( '连接到数据库失败。' , { cause : err }); 
  } 
} catch (err) {
  控制台. 日志(错误。原因);// ReferenceError: connectToDatabase 未定义
}

Future(已经可以与 TypeScript 4.9 一起使用)

  • 自动访问器:自动将属性设为私有并为其创建获取/设置访问器。
 Person {
  访问者名称字符串

  构造函数名称:字符串){
    这个名字=名字;
    控制台log ( this.name ) // 'test'   } } const person = new Person ( ' test' ) ;



 

打字稿

基础知识(进一步介绍的上下文)

  • 泛型:将类型传递给其他类型。这允许类型被泛化但仍然是类型安全的。总是喜欢这个而不是使用anyor unknown
// WITHOUT: 
function  getFirstUnsafe ( list: any [] ): any { 
  return list[ 0 ]; 
} 

const firstUnsafe = getFirstUnsafe ([ 'test' ]); // 键入任何

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

const first = getFirst< string >([ 'test' ]); // 输入为字符串

// 在这种情况下,参数甚至可以被删除,因为它是从参数中推断出来的。
const firstInferred = getFirst ([ 'test' ]); // typed as string 

// 接受为泛型的类型也可以使用 `extends` 进行限制。Type 通常也缩写为 T。class 
List  < T extends  string | number > {
  私有 列表:T[] = []; 

  得到数字):T {
    返回 这个列表[键];
  }

  价值:T):无效{
    这个列表(值);
  } 
} 

const list = new  List <字符串>(); 
列表。( 9 ); // 类型错误:“数字”类型的参数不可分配给“字符串”类型的参数。
const booleanList = new  List < boolean >(); // 类型错误:类型 'boolean' 不满足约束 'string | 数字'。

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

  • 实用程序类型:TypeScript 包含许多实用程序类型,这里解释了一些最有用的类型。
接口 测试{
  名称字符串
  年龄人数
} 

// Partial 实用程序类型使所有属性都是可选的。
type  TestPartial = Partial <测试>; // 输入为 { name?: string | 不明确的; 年龄?:数字 | 不明确的; } 
// Required 实用程序类型做相反的事情。
类型 TestRequired = Required < TestPartial >; // 输入为 { name: string; 年龄:数字;} 
// Readonly 实用程序类型使所有属性只读。
类型 TestReadonly =只读<测试>;// 类型为 { readonly name: string; readonly age: string } 
// Record 实用程序类型允许简单定义对象/地图/字典。最好尽可能索引签名。
const  config : Record < string , boolean > = { option : false , anotherOption : true }; 
// Pick 实用程序类型仅获取指定的属性。
输入 TestLess = Pick < Test , 'name' >; // 输入为 { name: string; }
输入 TestBoth = Pick < Test , 'name' | '年龄' >; // 输入为 { name: string; 年龄:字符串;} 
// Omit 实用程序类型忽略指定的 properties.type 
type  TestFewer = Omit < Test , 'name' >; // 输入为 { age: string; } 
type  TestNone = Omit < Test , '名称' | '年龄' >; // typed as {} 
// Parameters 实用程序类型获取函数类型的参数。
 value: string , anotherValue: number ): string { 
  return  'test' ; 
} 
type  Params = Parameters < typeof doSmth>; // typed as [value: string, anotherValue: number] 
// ReturnType 实用程序类型获取函数类型的返回类型。
type  Return = ReturnType < typeof doSmth>; // typed as string 

// 还有很多,下面会介绍其中的一些。
  • 条件类型:有条件地根据某种类型是否匹配/扩展另一种类型来设置类型。它们可以像 JavaScript 中的条件(三元)运算符一样阅读。
// 如果是数组,则只提取数组类型,否则返回相同类型。
输入 Flatten <T> = T extends  any [] ? T[] : T; 

// 提取出元素类型。
类型 Str = Flatten < string []>; // 键入为字符串

// 单独保留类型。
type  Num = Flatten < number >; // 输入为数字
  • 用条件类型推断:不是所有的泛型类型都需要消费者指定,有些也可以从代码中推断出来。要具有基于推断类型的条件逻辑,infer需要关键字。它以某种方式定义了临时推断类型变量。
// 从前面的例子开始,这样可以写得更干净。
type  FlattenOld <T> = T extends  any [] ? T[] : T; 

// 除了索引数组之外,我们还可以从数组中推断出 Item 类型。
输入 Flatten <T> = T extends (infer Item )[] ? 项目: T; 

// 如果我们想写一个类型来获取函数的返回类型,否则是未定义的,我们也可以推断。
类型 GetReturnType < Type > = Type  extends (... args : any[]) => 推断返回返回未定义

输入 Num = GetReturnType < () =>  number >; // 输入为数字

type  Str = GetReturnType < ( x: string ) =>  string >; // 类型为字符串

类型 Bools = GetReturnType < ( a: boolean , b: boolean ) =>  void >; // 类型为未定义
  • 元组可选元素和 Rest:声明元组中的可选元素 using ?,其余基于另一种类型 using ...
// 如果我们还不知道一个元组有多长,但它至少是一个,我们可以使用 `?` 指定可选类型。
const  list : [ number , number ?, boolean ?] = []; 
list[ 0 ] // 输入为数字
list[ 1 ] // 输入为数字 | undefined
 list[ 2 ] // 类型为布尔值 | undefined
 list[ 3 ] // 类型错误:长度为“3”的元组类型“[number, (number | undefined)?, (boolean | undefined)?]”在索引“3”处没有元素。

// 我们也可以将元组基于现有类型。
// 如果我们想在开始时填充一个数组,我们可以使用剩余运算符 `...` 来做到这一点。
函数padStart<T extends  any []>( arr : T, pad : string ): [ string , ...T] { 
  return [pad, ...arr]; 
} 

const padded = padStart ([ 1 , 2 ], 'test' ); // 输入为 [string, number, number]
  • 抽象类和方法:可以声明类和其中的方法以abstract防止它们被实例化。
抽象  Animal { 
  abstract  makeSound (): void ; 

  移动():无效{
    控制台日志'漫游地球......'); 
  } 
} 

// 扩展时需要实现抽象方法。
class  Cat  extends  Animal {} // 编译错误:非抽象类“Cat”未实现从类“Animal”继承的抽象成员“makeSound”。

  扩展 动物{ 
  makeSound){
    控制台日志( 'woof' ); 
  } 
} 

// 抽象类不能被实例化(如接口),抽象方法也不能被调用。
 动物();// 编译错误:无法创建抽象类的实例。

const dog = new  Dog ()。制作声音();//“汪”
  • 构造函数签名:在类声明之外定义构造函数的类型。在大多数情况下不应使用,可以使用抽象类代替。
接口 MyInterface {
  名称字符串
} 

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

 测试 实现 MyInterface {
  名称字符串
  构造函数名称:字符串){
    这个名字=名字;
  } 
} 

class  AnotherTest { 
  age : number ; 
}

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

const obj = makeObj (测试); // 类型为 Test 
const anotherObj = makeObj ( AnotherTest ); // 类型错误:“typeof AnotherTest”类型的参数不可分配给“ConstructsMyInterface”类型的参数。
  • ConstructorParameters 实用程序类型:TypeScript 辅助函数,它从构造函数类型(但不是类)获取构造函数参数。
// 如果我们想获取 makeObj 函数的构造函数参数怎么办。
接口 MyInterface {
  名称字符串
} 

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

 测试 实现 MyInterface {
  名称字符串
  构造函数名称:字符串){
    这个名字=名字;
  } 
}

函数 makeObj (测试: ConstructsMyInterface, ...args: ConstructorParameters<ConstructsMyInterface> ) {
  返回  测试(...args); 
} 

makeObj (测试); // 类型错误:需要 2 个参数,但得到 1 个
。const obj = makeObj ( Test , 'test' ); // 类型为测试

打字稿 4.0

  • 可变元组类型:元组中的其余元素现在可以是通用的。现在也允许使用多个 rest 元素。
// 如果我们有一个组合两个未定义长度和类型的元组的函数怎么办?我们如何定义返回类型?

// PREVIOUSLY: 
// 我们可以写一些重载。
声明 函数 concat ( arr1: [], arr2: [] ): []; 
声明 函数concat<A>( arr1 : [A], arr2 : []): [A]; 
声明 函数concat<A, B>( arr1 : [A], arr2 : [B]): [A, B]; 
声明 函数concat<A, B, C>( arr1 : [A], arr2 : [B, C]): [A, B, C]; 
声明 函数concat<A, B, C, D>(arr1 : [A], arr2 : [B, C, D]): [A, B, C, D]; 
声明 函数concat<A, B>( arr1 : [A, B], arr2 : []): [A, B]; 
声明 函数concat<A, B, C>( arr1 : [A, B], arr2 : [C]): [A, B, C]; 
声明 函数concat<A, B, C, D>( arr1 : [A, B], arr2 : [C, D]): [A, B, C, D]; 
声明 函数concat<A, B, C, D, E>( arr1 : [A, B], arr2 : [C, D, E]): [A, B, C, D, E]; 
声明 函数concat<A, B, C>( arr1: [A, B, C], arr2 : []): [A, B, C]; 
声明 函数concat<A, B, C, D>( arr1 : [A, B, C], arr2 : [D]): [A, B, C, D]; 
声明 函数concat<A, B, C, D, E>( arr1 : [A, B, C], arr2 : [D, E]): [A, B, C, D, E]; 
声明 函数concat<A, B, C, D, E, F>( arr1 : [A, B, C], arr2 : [D, E, F]): [A, B, C, D, E, F ]; 
// 即使每个只有三个项目,这也不是最理想的。

// 相反,我们可以组合类型。
声明 函数concatBetter<T, U>( arr1 : T[], arr2: U[]): (T | U)[]; 
// 但这种类型为 (T | U)[] 

// 新:
// 使用可变元组类型,我们可以轻松定义它并保留有关长度的信息。
声明 函数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 ]作为[数字,数字,数字]); 
控制台日志(元组[ 0 ]);// 23 
const 元素: number = tuple[ 1 ]; // 类型错误:类型“string”不可分配给类型“number”。
控制台日志(元组[ 6 ]);// 类型错误:长度为 '6' 的元组类型 '[23, "hey", false, 5, 99, 20]' 在索引 '6' 处没有元素。
  • 标记的元组元素:元组元素现在可以命名为[start: number, end: number]如果其中一个元素被命名,则所有这些元素都必须被命名。
输入 Foo = [ first : number , second?: string , ... rest : any []]; 

// 这允许在这里正确命名参数,它也会显示在编辑器中。
声明 函数 someFunc ( ...args: Foo );
  • 从构造函数推断类属性:在构造函数中设置属性时,现在可以推断类型,不再需要手动设置。
class  Animal { 
  // 在构造函数中赋值时不需要设置类型。
  姓名; 

  构造函数名称:字符串){
    这个名字=名字;
    控制台日志这个名字);// 类型为字符串
  } 
}
  • JSDoc @deprecated 支持:JSDoc/TSDoc@deprecated标签现在可以被 TypeScript 识别。
/** @deprecated消息 */ 
type  Test = string ; 

常量 测试测试= 'dfadsf'// 类型错误:“测试”已弃用。

打字稿 4.1

  • 模板字面量类型:定义字面量类型时,可以通过模板来指定类型,例如${Type}这允许构造复杂的字符串类型,例如在组合多个字符串文字时。
type  VerticalDirection = '顶部' | '底部' ; 
类型 Horizo​​ntalDirection = '左' | '对' ; 
输入 Direction = ` ${VerticalDirection}  ${Horizo​​ntalDirection} ` ; 

const  dir1 : Direction = '左上角' ; 
const  dir2 : Direction = 'left' ; // 类型错误:类型“left”不可分配给类型“top left” | “右上角” | “左下角” | 
 Direction = '左上角' ; // 类型错误:类型 '"left top"' 不可分配给类型 '"top left" | “右上角” | “左下角” | “右下”。

// 这也可以与泛型和新的实用程序类型结合使用。
declare  function makeId<T extends  string , U extends  string >( first : T, second : U): ` ${Capitalize<T>} - ${Lowercase<U>} ` ;
  • 映射类型中的键重映射:重新键入映射类型,同时仍然使用它们的值,如[K in keyof T as NewKeyType]: T[K].
// 假设我们想重新格式化一个对象,但在其 ID 前加上下划线。
const obj = { value1 : 0 , value2 : 1 , value3 : 3 }; 
const  newObj : { [ keyof typeof obj中的属性 as `_ ${Property} ` ]: number }; // 输入为 { _value1: number; _value2:数字;值 3:数字;} 
  • 递归条件类型:在其定义本身内部使用条件类型。这允许有条件地解包无限嵌套值的类型。
输入 Awaited <T> = T extends  PromiseLike <infer U> ? 等待<U> : T; 

输入 P1 = Awaited <字符串>; // 输入为字符串
类型 P2 = Awaited < Promise < string >>; // 输入为字符串
类型 P3 = Awaited < Promise < Promise < string >>>; // 输入为字符串
  • JSDOC @see 标签的编辑器支持@see variable/type/link:编辑器现在支持JSDoc/TSDoc标签。
const原始值 = 1 ; 
/** 
  * 另一个值的副本
  * @see  originalValue
   */ 
const value = originalValue;
  • tsc --explainFiles:该--explainFiles选项可用于 TypeScript CLI 以解释哪些文件是编译的一部分以及原因。这对于调试很有用。警告:对于大型项目或复杂的设置,这将生成大量输出,而不是使用tsc --explainFiles | less或类似的东西。
tsc --explainFiles 

<< output 
../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es5.d.ts
  通过文件中的“es5”引用的库'../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2015.d.ts'
  通过文件 '../.es5' 引用的库。 ./.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
  库通过'es2015'从文件'../../.asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/ typescript/lib/lib.es2016.d.ts'
  通过文件 '../../.asdf/installs/nodejs/16.13.1/ 中的 'es2015' 引用的库。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
  库通过'es2016'从文件'../../ .asdf/installs/nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2017.d.ts'
  通过'es2016'从文件'../../.asdf/installs/引用的库nodejs/16.13.1/.npm/lib/node_modules/typescript/lib/lib.es2017.d.ts' 
...
输出
  • Destructured Variables Can Be Explicitly Marked as Unused:解构时,可以使用下划线将变量标记为未使用。这可以防止 TypeScript 抛出“未使用的变量”错误。
const [_first, second] = [ 3 , 5 ]; 
控制台日志(第二);

// 或者更短
const [_, value] = [ 3 , 5 ]; 
控制台日志(值);

打字稿 4.3

  • 属性上的单独写入类型:定义设置/获取访问器时,写入/设置类型现在可以不同于读取/获取类型。这允许设置器接受相同值的多种格式。
 测试{ 
  private  _value : number ; 

  获取 ():数字{
    返回 这个_值
  }

  设置 值:数字|字符串){
    如果类型值 === '数字'){
      _value = 值;
      返回
    }
    这个_value = parseInt(值,10);
  } 
}
  • override:将继承的类方法显式标记为 overrides using override,这样当父类发生变化时,TypeScript 可以通知您父方法不再存在。这允许更安全的复杂继承模式。
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 ():字符串{ // 类型错误:该成员不能有 'override' 修饰符,因为它未在基类 'NewParent' 中声明。
    返回 “测试”
  } 
}
  • 静态索引签名:在类上使用静态属性时,现在也可以使用static [propName: string]: string.
// 以前:
 测试{}

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

// 新:
 NewTest { 
  static [ key : string ]: string ; 
}

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

const value = originalValue;

打字稿 4.4

  • 精确可选属性类型 (--exactOptionalPropertyTypes):使用编译器标志--exactOptionalPropertyTypes(或 in tsconfig.json)赋值undefined不再允许隐式允许的属性undefined(例如property?: string)。相反undefined需要明确允许像property: string | undefined.
 测试{
  名称?:字符串; 
  年龄:号码| 未定义
} 

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

打字稿 4.5

  • Awaited 类型和 Promise 改进:新的Awaited<>实用程序类型从无限嵌套的 Promises 中提取值类型(就像await对值所做的那样)。这也改进了Promise.all().
// 假设我们想要一个通用的等待值。
// 我们可以为此使用 Awaited 实用程序类型(它的源代码是前面示例的一部分),
// 因此无限嵌套的 Promises 都解析为它们的值。
输入 P1 = Awaited <字符串>; // 输入为字符串
类型 P2 = Awaited < Promise < string >>; // 输入为字符串
类型 P3 = Awaited < Promise < Promise < string >>>; // 输入为字符串
  • 导入名称上的类型修饰符:在普通(非import type)导入语句中,type关键字可用于表示该值只应为类型编译导入(并且可以删除)。
// PREVIOUSLY: 
// 导入类型的最佳方法是使用 `import type` 关键字来防止它们在编译后实际被导入。'./file'
导入{ something} './file'导入类型{ SomeType } // 对于同一个文件,这需要两个导入语句。// 新:// 现在这可以组合成一个语句。import { something, type SomeType } from './file' ; 
  




  
  • const Assertions:定义常量时as const可用于将它们准确地键入为文字类型。这有很多用例,可以更轻松地进行准确的输入。它还生成 objects 和 arrays readonly,以防止常量对象发生突变。
// PREVIOUSLY: 
const obj = { name : 'foo' , value : 9 , toggle : false }; // 输入为 { name: string; 值:数字;切换:布尔值;} 
// 任何值都可以赋值,因为它们通常是有类型的。
对象。名称= '酒吧' ; 

const tuple = [ 'name' , 4 , true ]; // typed as (string | number | boolean)[] 
// 无法从类型中确定长度和确切类型。可以在任何地方分配任何值。
元组[ 0 ] =0 ; 
元组[ 3 ] = 0 ; 

// 新:
const objNew = { name : 'foo' , value : 9 , toggle : false } as  const ; // 输入为 { readonly name: "foo"; 只读值:9;只读切换:假;} 
// 不能赋值(因为它被定义为“foo”(而且也是只读的))。
对象新建。名称= '酒吧' ; // 类型错误:无法分配给“名称”,因为它是只读属性。

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

打字稿 4.6

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

// Record 从允许的类型中指定种类和值类型。
type  UnionRecord < AllowedKeys  extends keyof AllowedTypes > = { [ Key  in  AllowedKeys ]: 
{ 
  kind : Key ; 
  AllowedTypes [];
  日志:(值:AllowedTypes[Key] ) =>  void ; 
}
}[允许键]; 

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

 processRecord ({
   kind : 'string' ,
   value : 'hello!' ,
   // 用于隐式具有类型 string | number | boolean 的值,
  // 但现在被正确地推断为字符串。
  日志值=> {
    控制台log (value.toUpperCase ( )); 
  } 
});
  • TypeScript Trace Analyzer (--generateTrace):该--generateTrace <Output folder>选项可用于 TypeScript CLI 生成包含有关类型检查和编译过程的详细信息的文件。这有助于优化复杂类型。
tsc --generateTrace trace 

cat trace/trace.json 
<<输出
[ 
{"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":"
禁用-by-default-devtools.timeline","ph":"M","ts":...,"pid":1,"tid":1}, {"pid":1,"tid" 
: 1,“酸碱度”:"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":"/..."}},"rootDir":"/..."}}, {"pid":1,"tid":1,"ph":"B","cat":"parse","ts":..., "name":"createSourceFile","args":{"path":"/..."}}, {"pid":1,"tid":1,"ph":"E","cat" :"解析","ts":...,"名称":"createSourceFile","args":{"路径":"/..."}},"rootDir":"/..."}}, {"pid":1,"tid":1,"ph":"B","cat":"parse","ts":..., "name":"createSourceFile","args":{"path":"/..."}}, {"pid":1,"tid":1,"ph":"E","cat" :"解析","ts":...,"名称":"createSourceFile","args":{"路径":"/..."}},cat":"parse","ts":...,"name":"createSourceFile","args":{"path":"/..."}},cat":"parse","ts":...,"name":"createSourceFile","args":{"path":"/..."}},
{“pid”:1,“tid”:1,“ph”:“X”,“cat”:“程序”,“ts”:...,“name”:“resolveModuleNamesWorker”,“dur”:。 ..,"args":{"containingFileName":"/..."}}, 
...
输出

cat trace/types.json 
<<输出
[{"id":1,"intrinsicName":"any", "recursionId":0,"flags":["..."]}, 
{"id":2,"intrinsicName":"any","re​​cursionId":1,"flags":["..." ]}, 
{"id":3,"intrinsicName":"any","re​​cursionId":2,"flags":["..."]}, {"id":4,"intrinsicName":" 
error ","recursionId":3,"flags":["... 
"]}, {"id":5,"intrinsicName":"unresolved","re​​cursionId":4,"flags":["..." ]}, 
{"id":6,"intrinsicName":"any","re​​cursionId":5,"flags":["..."]}, {"id":7,"intrinsicName":"
内在","re​​cursionId":6,"flags":["..."]},intrinsicName":"intrinsic","re​​cursionId":6,"flags":["..."]},intrinsicName":"intrinsic","re​​cursionId":6,"flags":["..."]},
{"id":8,"intrinsicName":"unknown","re​​cursionId":7,"flags":["..."]}, {"id":9,"intrinsicName":"unknown", 
" recursionId":8,"flags":["..."]}, 
{"id":10,"intrinsicName":"undefined","re​​cursionId":9,"flags":["..."] }, 
{"id":11,"intrinsicName":"undefined","re​​cursionId":10,"flags":["..."]}, {"id":12,"intrinsicName":"null 
" ,"recursionId":11,"flags":["..."]}, 
{"id":13,"intrinsicName":"string","re​​cursionId":12,"flags":["... “]}, 
...
输出

打字稿 4.7

  • Node.js 中的 ECMAScript 模块支持:当使用 ES 模块而不是 CommonJS 时,TypeScript 现在支持指定默认值。tsconfig.json.
...
“compilerOptions”  [
   ... 
  “模块”  “es2020” 
]
 ...
  • type in package.jsontype : in 的字段package.json可以设置为"module",这是使用 node.js 和 ES Modules 所需要的。在大多数情况下,这对 TypeScript 来说已经足够了,不需要上面的编译器选项。
...
“类型”  “模块”
 ...
  • 实例化表达式:实例化表达式允许在引用值时指定类型参数。这允许在不创建包装器的情况下缩小泛型类型。
 列表<T> {
  私有 列表:T[] = []; 

  得到数字):T {
    返回 这个列表[键];
  }

  价值:T):无效{
    这个列表(值);
  } 
} 

function makeList<T>( items : T[]): List <T> { 
  const list = new  List <T>(); } 
  项目。forEach (项目=>列表。(项目));
  返回列表;
} 

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

// 新:
// 使用实例化表达式,这要容易得多。
const makeNumberList = makeList<数字>;
  • extends Constraints on infer Type Variables:在条件类型中推断类型变量时,现在可以使用extends.
// 假设我们想要输入一个类型,如果它是一个字符串,它只获取数组的第一个元素。
// 我们可以为此使用条件类型。

// 以前:
type  FirstIfStringOld <T> = 
  T extends [infer S, ... unknown []] 
    ? S扩展 字符串? S:从不
从不

// 但这需要两个嵌套的条件类型。我们也可以合二为一。
type  FirstIfString <T> = 
  T extends [ string , ... unknown []] 
    // 从 `T` 中获取第一个类型
T[ 0] 
从不

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

// 新:
// 在推断类型变量上使用扩展约束,这可以更容易地声明。
输入 FirstIfStringNew <T> = 
  T extends [infer S extends  string , ... unknown []] 
    ? S 
从不
// 请注意,输入的工作方式与之前相同,这只是一种更简洁的语法。

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

interface  Dog  extends  Animal { 
  dogStuff : any ; 
} 

// 我们有一些通用的“getter”和“setter”。
输入 Getter <T> = () => T; 

类型 Setter <T> = ( value: T ) =>  void ; 

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

// 现在我们可以将 Getter 传递给函数。
useAnimalGetter (( () => ({ animalStuff : 0 })作为 动物)); 
// 这显然有效。

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

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

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

useDogGetter (( () => ({ animalStuff : 0 , dogStuff : 0 })作为 ));
// 但是,这是有效的。

// 直觉上,我们可能希望 Setter 的行为相同,但事实并非如此。
函数 setAnimalSetter ( setter: Setter<Animal>, value: Animal ) { 
  setter (value); 
} 

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

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

// 这里也一样。
设置狗设置器价值:狗)=> {},{ an​​imalStuff0dogStuff0 }); 

// 但如果我们将 Dog Setter 传递给 setAnimalSetter 函数,则行为与 Getters 相反。
setAnimalSetter ( ( value: Dog ) => {}, { animalStuff : 0 , dogStuff : 0 }); // 类型错误:'(value: Dog) => void' 类型的参数不可分配给'Setter<Animal>' 类型的参数。

// 这次它以相反的方式工作。
setDogSetter值:动物)=>{}, { animalStuff : 0 , dogStuff : 0 }); 

// 新:
// 要向 TypeScript 发出信号(不需要但有助于提高可读性),请使用新的类型参数可选方差注释。
输入 GetterNew <out T> = () => T; 
输入 SetterNew < in T> = ( value: T ) =>  void ;
  • 分辨率自定义moduleSuffixes:当使用具有自定义文件后缀的环境(例如.ios本地应用程序构建)时,现在可以为 TypeScript 指定这些后缀以正确解析导入。tsconfig.json.
...
"compilerOptions" :  [
   ... 
  "moduleSuffixes" :  [ ".ios" ,  ".native" ,  "" ] 
]
 ...
'./foo'导入*作为foo // 这首先检查 ./foo.ios.ts、./foo.native.ts,最后检查 ./foo.ts。 
  • 在编辑器中转到源定义:在编辑器中,新的“转到源定义”菜单选项可用。它类似于“转到定义”,但更喜欢.ts.js文件而不是类型定义(.d.ts)。
   

打字稿 4.9

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

// 这会隐式键入属性,以便我们可以对数组和字符串进行操作。
const rgb1 = 对象。救火车[ 0 ];// 类型为数字
const十六进制=对象。灌木丛// 类型为字符串

// 假设我们只想允许某些对象。
// 我们可以使用 Record 类型。
const  oldObj : Record <字符串, [数字,数字,数字] | string > = { 
  fireTruck : [ 255 , 0 , 0 ], 
  bush : '#00ff00' , 
  ocean : [ 0 , 0 , 255 ] 
}// 输入为 Record<string, [number, number, number] | string> 
// 但现在我们丢失了属性的类型。
常量oldRgb1 = oldObj。救火车[ 0 ];// 类型为字符串 | 数字
常量oldHex = oldObj。灌木丛// 类型为字符串 | number 

// 新:
// 使用 satisfies 关键字,我们可以检查与类型的兼容性而无需实际分配它。
const newObj = { 
  fireTruck : [ 255 , 0 , 0 ], 
  bush : '#00ff00' , 
  ocean : [ 0 , 0, 255 ] 
} 满足Record < string , [ number , number , number ] | string > // 输入为 { fireTruck: [number, number, number]; 布什:字符串;海洋:[数字,数字,数字];} 
// 而且我们仍然有属性的类型,数组甚至通过成为元组变得更加准确。
const newRgb1 = newObj。救火车[ 0 ];// 输入为数字
const newRgb4 = newObj. 消防车[ 3 ];// 类型错误:长度为“3”的元组类型“[number, number, number]”在索引“3”处没有元素。
const newHex = newObj。灌木丛// 输入为字符串
  • 编辑器的“删除未使用的导入”和“排序导入”命令:在编辑器中,新命令(和自动修复)“删除未使用的导入”和“排序导入”使管理导入更加容易。
 

标签:JavaScript, 编程,Web开发
来源:

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

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

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

ICode9版权所有