和 [key: string]: 类型,typescript,typescript-utility"/>

首页 > 解决方案 > 记录之间的区别和 [key: string]: 类型

问题描述

我想知道 和 之间的Record<key, type>区别[key: string]: type。我们可以互换使用它们吗?哪个更动态和类型安全?

我有一个例子,我可以互换使用它们。使用哪一个是好的做法?

//Record example
interface PageInfo {
  title: string;
}

type Page = "home" | "about" | "contact";

const nav: Record<Page, PageInfo> = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" },
};

console.log(nav.about);
// object example

type Page2 = {
  [key: string]: PageInfo;
};

const navHtml: Page2 = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" },
};

console.log(navHtml.contact);

此示例的打字稿游乐场

标签: typescripttypescript-utility

解决方案


首先,AFAIK,它们都不是完全类型安全的。

看到这个问题

key您可以在 Record -中使用联合类型Record<Page, PageInfo>,而在索引类型中则不能:

type Page2 = {
  [key: string | number | symbol]: PageInfo; // error
};

例子

//Record example
interface PageInfo {
  title: string;
}

type Page = "home" | "about" | "contact";

const nav: Record<Page, PageInfo> = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" },
};

console.log(nav.about);
// object example

type Page2 = {
  [key: string]: PageInfo;
};

const navHtml: Page2 = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" },
};

console.log(navHtml.contact);

/**
 * If you have a string as a key - no problem
 */
type Test1 = Record<string, string> extends { [p: string]: string } ? true : false // true
type Test2 = { [p: string]: string } extends Record<string, string> ? true : false // true

const foo = (arg: Record<string, string>) => arg
const indexed: { [p: string]: string } = { bar: 'bar' }
foo(indexed) // no error

/**
 * And vice-versa
 */
const bar = (arg: { [p: string]: string }) => arg
const record: Record<string, string> = { bar: 'bar' }
foo(record) // no error

/**
 * But if you have a union type as a key
 * Above approach will not work
 */
type Test3 = Record<'a' | 'b', string> extends { [p: string]: string } ? true : false // true
type Test4 = { [p: string]: string } extends Record<'a' | 'b', string> ? true : false // false

const withIndex: Record<'a' | 'b', string> = { bar: 'bar' } // missing a,b

更新

/**
 * Explanation of Record<'a'|'b', string>
 * It is mean that objects should have a & b keys
 */

type CustomRecord = Record<'a' | 'b', string>

const allowedRecord: CustomRecord = {
  a: '1',
  b: '2'
} // ok

const allowedRecord2: CustomRecord = {
  a: '1',
} // error, because without b

const allowedRecord3: CustomRecord = {
  b: '1',
} // error, because without a

/**
 * You are unable to do same thing with indexed type
 */
type TypeIndexedWIthExplicitKeys = {
  [p: string | number]: string
}

interface InterfaceIndexedWIthExplicitKeys {
  [p: 'a' | 'b']: string
}

const x: InterfaceIndexedWIthExplicitKeys = { 2: 's' } // no error, but.... I would expect an error
const y: TypeIndexedWIthExplicitKeys = { 2: 's' } // no error, but.... I would expect an error


const check = (): InterfaceIndexedWIthExplicitKeys => {
  return { 2: 2 } // no error, but I would expect
}
type MyRecord = Record<'a' | 'b', string>

const z: MyRecord = { 2: '2' } // error, because we expect a & b
const c: MyRecord = { a: '2', b: '3' } // ok

推荐阅读