繁体   English   中英

如何在实用程序类型上定义属性?

[英]How to define a property on a utility type?


尝试通过任何可用的方式进行类型推断


所以我有一个将数组作为参数的函数。 该数组包含未知对象。 我们确实知道对象都是一样的,所以换句话说,数组只能接受一种类型的对象,但对象类型是动态的,它几乎可以是任何东西。 我说几乎是因为我们知道了一点信息:对象都有一个 name 属性。


总结:


该函数的数组参数接受一个类型相同但类型未知的对象数组。 我们所知道的是该类型包含一个名称属性,因此; 我们知道每个对象都有一个 name 属性。



好的,现在让我们看一下函数:


type ObjType = {
  name: string;
  [key: string]: any;
}

type NewObj = {
  [key: string]: {}
}

export function nameArray(arrParam: ObjType[]){
  let obj: NewObj = {};

  arrParam.forEach((item, index)=>{
    obj[item.name] = item;
  });

  return obj;
}


关于功能和类型


因为我们知道对象都有一个 name 属性,所以我们可以使用它为键分配适当的名称,然后将对象分配给该键。 这样,我们就有了可枚举的数组,以及一个可以用来按名称引用对象的对象。


功能非常好用!


好吧,我撒谎了,老实说,我不是故意的。 该功能确实有效,但我认为奇妙的是措辞的错误选择。 类型推断没有流经该函数,因此,即使它有效,我在使用它时也没有得到类型信息,这很糟糕,因为 TS 需要做很多额外的工作,如果我不这样做又有什么意义在脚本的复杂方面结束类型。


我在一个真实的项目中使用它。 这是我测试过的文档。

import { nameArray } from './util-fn.mjs';

type StrGenArg = {
    // INDEX @00
    name: string,
    arg: string,
    code: string | number;
  }

type StrGenArgs = StrGenArg[];

export const strGenArg: StrGenArgs = [
  { // INDEX @00 | "escOpen"
    name: 'escOpen',
    arg:  '%%',
    code: '\x1b'
  },
  { // INDEX @01 | "escClose"
    name: 'escClose',
    arg:  '%%',
    code: '\x1b'
  },
  { // INDEX @02 | "reset"
    name: 'reset',
    arg:  '0',
    code: 0
  },
  { // INDEX @03 | "red"
    name: 'red',
    arg:  '%r',
    code: 31
  },
  { // INDEX @04 | "green"
    name: 'green',
    arg:  'g',
    code: 32
  },
  { // INDEX @05 | "yellow"
    name: 'yellow',
    arg:  'y',
    code: 33
  }
];

export const args = nameArray(strGenArg);

正如我所说,它有效; 喜欢,我做以下。

console.log(args.yellow)

它打印黄色,红色和绿色以及其余对象相同,但是,正如我已经说过的:没有类型!

我尝试使用这样的实用程序类型。


function nameArray<T>(arrParam: T[]){
  let obj: NewObj = {};

  arrParam.forEach((item, index)=>{
    obj[item.name] = item;
  });
}

但是当我以这种方式编写函数时,它不知道T具有命名属性,因此,TSC 显示错误,并且不会编译。

有没有写这个函数,我可以得到类型推断?


您需要将T限制为始终具有属性name 这将强制传递给函数的数组的任何元素都必须具有name属性。

function nameArray<T extends { name: string }>(arrParam: T[]){ /* ... */ }

但这并不能完全解决您的问题。 您说您例如想要访问args.yellow 那么我们该怎么做呢?

这有点复杂。

首先,我们必须将as const添加到strGenArgs ,这样我们就不会在这里丢失类型信息:

export const strGenArg = [
  { // INDEX @00 | "escOpen"
    name: 'escOpen',
    arg:  '%%',
    code: '\x1b'
  },
  { // INDEX @01 | "escClose"
    name: 'escClose',
    arg:  '%%',
    code: '\x1b'
  },
  { // INDEX @02 | "reset"
    name: 'reset',
    arg:  '0',
    code: 0
  },
  { // INDEX @03 | "red"
    name: 'red',
    arg:  '%r',
    code: 31
  },
  { // INDEX @04 | "green"
    name: 'green',
    arg:  'g',
    code: 32
  },
  { // INDEX @05 | "yellow"
    name: 'yellow',
    arg:  'y',
    code: 33
  }
] as const;

我们还介绍了通用类型K ,它将保存所有可能的名称值。 之后我们返回一个Record<K, StrGenArg>

type ExtractName<T extends { name: string }[]> = T[number]["name"]

function nameArray<
  T extends { name: string }[], 
  K extends ExtractName<T>
>(arrParam: readonly [...T]): Record<K, StrGenArg> {
  let obj: Record<string, any> = {};

  arrParam.forEach((item, index)=>{
    obj[item.name] = item;
  });

  return obj
}

现在打字工作:

export const args = nameArray(strGenArg);

args.yellow // works with typing

操场

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM