[TypeScript] Model a DataStore type
Given this code as starter:
export interface DataEntity {
id: string
}
export interface Movie extends DataEntity {
director: string
}
export interface Song extends DataEntity {
singer: string
}
export type DataEntityMap = {
movie: Movie
song: Song
}
export class DataStore {}
This DataEntityMap
object should drive a lot of what happens to DataStore
.
Ultimately, DataStore
should end up with methods like
const ds = new DataStore()
ds.addSong({ id: "song-123", singer: "The Flaming Lips" })
ds.addMovie({
id: "movie-456",
director: "Stephen Spielberg",
})
ds.getSong("song-123") // returns the song
ds.getMovie("movie-456") // returns the movie
ds.getAllSongs() // array of all songs
ds.getAllMovies() // array of all movies
ds.clearSongs() // clears all songs
ds.clearMovies() // clears all movies
It’s ok to define these explicitly in the DataStore
class, but they should be type-checked against the DataEntityMap
type in some way.
Requirements
- If you mis-name a method on the class (e.g.,
getSongs
instead ofgetAllSongs
), you should get some sort of type error that alerts you that you’ve broken the established pattern - If you add a new entity like
Comic
(shown below) and make no other changes to your solution, you should get some sort of type error that alerts you to the absence of aclearComics
,getAllComics
andgetAllSongs
method.
- There should be no externally-visible properties on an instance of
DataStore
beyond the required methods - Your code, and the test suite should type-check
.
.
.
.
.
.
.
.
Answer
export interface DataEntity {
id: string
}
export interface Movie extends DataEntity {
director: string
}
export interface Song extends DataEntity {
singer: string
}
export interface Comic extends DataEntity {
issueNumber: number
}
export type DataEntityMap = {
movie: Movie
song: Song
}
/**
* if you change DataEntityMap from type to interface
* Then you can have the DataEntityMap defined in multi files and TS will combine those automaticlly
// model/song.model.ts
export interface DataEntityMap {
song: Song;
}
// model/movie.model.ts
export interface DataEntityMap {
movie: Movie;
}
*/
export type DataStoreMethods = {
[K in keyof DataEntityMap as `getAll${Capitalize<K>}s`]: () => DataEntityMap[K][]
} & {
[K in keyof DataEntityMap as `get${Capitalize<K>}`]: (id: string) => DataEntityMap[K]
} & {
[K in keyof DataEntityMap as `clear${Capitalize<K>}s`]: () => void
}
& {
[K in keyof DataEntityMap as `add${Capitalize<K>}`]: (item: DataEntityMap[K]) => DataEntityMap[K]
}
function isDefined<T>(x: T | undefined): x is T {
return typeof x !== "undefined";
}
export class DataStore implements DataStoreMethods {
#data: {[K in keyof DataEntityMap]: Record<string, DataEntityMap[K]>} = {
movie: {},
song: {}
}
getSong(id: string): Song {
const song = this.#data.song[id];
if (!song) {
throw new Error(`Could not find a song with id ${id}`);
}
return song;
}
getAllSongs(): Song[] {
return Object.keys(this.#data.song).map(id => this.#data.song[id]).filter(isDefined)
}
clearSongs(): void {
this.#data.song = {}
}
addSong(song: Song) {
this.#data.song[song.id] = song;
return song;
}
getMovie(id: string): Movie {
const movie = this.#data.movie[id];
if (!movie) {
throw new Error(`Could not find a movie with id ${id}`);
}
return movie;
}
getAllMovies(): Movie[] {
return Object.keys(this.#data.movie).map(id => this.#data.movie[id]).filter(isDefined)
}
clearMovies(): void {
this.#data.movie = {}
}
addMoive(movie: Movie) {
this.#data.movie[movie.id] = movie;
return movie;
}
}
分类:
TypeScript
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2021-07-13 [SAA + SAP] 03. EC2