我想输入一个数组,这样数组中唯一允许的值就是对象列表中某个键的值.

原则上,我知道这可以这样做:

const page1 = {
  videoTitles: ['someTitle', 'otherTitle'],
} as const

const page2 = {
  videoTitles: ['title', 'goodTitle'],
} as const

const allVideos = [...page1.videoTitles, ...page2.videoTitles]

// As desired, the if statement raises an error because 'badTitle' isn't in either of the lists
if (allVideos.includes('badTitle')) {
  console.log('Found!')
}

然而,我想要做的是为对象赋予一个类型并获得相同的行为,但我就是想不出如何做到这一点.以下是我想要的大致内容:

type PageWithVideos = { readonly videoTitles: readonly string[] }
const page1: PageWithVideos = {
  videoTitles: ['someTitle', 'otherTitle'],
} as const

const page2: PageWithVideos = {
  videoTitles: ['title', 'goodTitle'],
} as const

const allTitles = [...page1.videoTitles, ...page2.videoTitles]

// Now, however, I don't get the same error. allTitles is just a string[]
if (allTitles .includes('badTitle')) {
  console.log('Found!')
}

我发现我真的可以用这样的东西force它:

type PageWithVideos<T extends string> = { readonly videoTitles: readonly T[] }

const page1: PageWithVideos<'someTitle' | 'otherTitle'> = {
  videoTitles: ['someTitle', 'otherTitle'],
} as const

const page2: PageWithVideos<'title' | 'goodTitle'> = {
  videoTitles: ['title', 'goodTitle'],
} as const

const allVideos = [...page1.videoTitles, ...page2.videoTitles] as const

// I'm getting the error again (which is good), but this is a little silly
if (allVideos.includes('badTitle')) {
  console.log('Found!')
}

但我更希望不必重复列表中的每个标题和通用名称.如有任何 idea ,我们将不胜感激!


编辑-使用 case :

建立一个只限FE的纪录片组合网站.这些视频托管在外部视频平台(YouTube、Vimeo)上,并嵌入到网站中.

比如说,客户有100个视频.这些视频有大约7种不同的观看方式.客户希望能够在每个屏幕上 Select 显示哪些视频以及显示的顺序.许多视频将出现在多个屏幕上.

我想提供一个解决方案,其中客户可以将Video个对象添加到大约videos.ts个文件中.添加视频时,会判断这些值的类型.然后,我希望客户转到另一个文件(recetWork.ts),并添加他想要在该页面上显示的视频的标题.但我真正想要的是recentWork.ts上的标题列表与videos.ts上所有视频名称的列表进行类型判断.

// videos.ts
export const videos: Video[] = [{
  title: 'someTitle',
  ...
}, {
  title: 'title',
  ...
}]


// recentWorks.ts
export const recentWorkPageInfo = {
  videoTitles: ['title', 'someTitle'], // type-checked against `title` key from videos
  ...
}

解决方案:@ghybs得到了它!

// videos.ts
export const videos = [{
  title: 'someTitle',
  ...
}, {
  title: 'title',
  ...
}] as const satisfies Video[]


// recentWorks.ts
import { videos } from '../videos'
type PageInfo = {
  videoTitles: (typeof videos)[number]['title'][]
}
export const recentWorkPageInfo = {
  videoTitles: ['title', 'someTitle'], // type-checked!! 
  ...
}

推荐答案

这听起来像是satisfies操作符的用例(在打字脚本4.9中介绍):

新的satisfies运算符允许我们验证表达式的类型是否与某个类型匹配,而无需更改该表达式的结果类型.

因此,您可以捕获不符合PageWithVideos形状的对象:

const page3 = {
    videoTitles: [0], // Error: Type 'number' is not assignable to type 'string'.
    //            ~
} as const satisfies PageWithVideos

const page4 = {
    titles: ["badKey"], // Error: Object literal may only specify known properties, and 'titles' does not exist in type 'PageWithVideos'.
//  ~~~~~~
} as const satisfies PageWithVideos

...但是它们的类型仍然被正确地推断出来(特别是从它们的内容中推断出它们的文字类型,这要归功于as const断言),并且您可以保持您想要的行为:

const page1 = {
    videoTitles: ['someTitle', 'otherTitle'],
} as const satisfies PageWithVideos

const page2 = {
    videoTitles: ['title', 'goodTitle'],
} as const satisfies PageWithVideos

const allTitles = [...page1.videoTitles, ...page2.videoTitles]

if (allTitles.includes('badTitle')) { // Error: Argument of type '"badTitle"' is not assignable to parameter of type '"someTitle" | "otherTitle" | "title" | "goodTitle"'.
    //                 ~~~~~~~~~~
    console.log('Found!')
}

Playground Link

Typescript相关问答推荐

如何将http上下文附加到angular中翻译模块发送的请求

如何传递基于TypScript中可变类型的类型?

异步动态生成Playwright测试,显示未找到测试

类型脚本满足将布尔类型转换为文字.这正常吗?

ANGLE独立组件布线错误:没有ActivatedRouting提供程序

扩展参数必须具有元组类型或传递给REST参数

是否将自动导入样式从`~/目录/文件`改为`@/目录/文件`?

复选框Angular 在Ngfor中的其他块中重复

为什么S struct 类型化(即鸭子类型化)需要非严格类型联合?

创建一个TypeScrip对象并基于来自输入对象的约束定义其类型

Typescript,如何在通过属性存在过滤后重新分配类型?

如何在打字角react 式中补齐表格组

如何避免多个类型参数重复?

如何实现允许扩展泛型函数参数的类型

为什么我的 Typescript 函数中缺少 void 隐式返回类型?

在Typescript 中,是否有一种方法来定义一个类型,其中该类型是字符串子集的所有可能组合?

const 使用条件类型时,通用类型参数不会推断为 const

React 中如何比较两个对象数组并获取不同的值?

req.files = 未定义(Multer、Express、Typescript)

从接口推断模板参数?