Typescript: выводить правильный тип из строкового литерального объединения, примененного к объекту с ключом - PullRequest
1 голос
/ 13 июня 2019

Я использую MobX, и у меня есть этот репозиторий, содержащий все сущности в моем приложении.

Я хочу иметь две функции

  • addEntity: Принимает сущность и обновляет / вставляет ее
  • getEntity: Возвращает сущность, если она найдена, иначе не определена

Но я также хочу, чтобы они были безопасны.

Допустим, я храню эту коллекцию:

@observable entities = {
   authors: {} as Record<string, Author>,
   comments: {} as Record<string, Comment>,
   posts: {} as Record<string, Post>
}

Я хочу использовать свою функцию следующим образом:

// "authors" should be checked against "authors" | "posts" | "comments"
// by specifying "authors" statically, I want typescript to automatically refine the return type to be Author, otherwise Author | Posts | Comment
addEntity("authors", new Author(...)) 

// same here
getEntity("authors", id) 

Я пробовал много способов с генериками, но я не могу понять это правильно. Я должен добавить общий тип, который я печатаю Поэтому мои звонки таковы:

getEntity<Author>("authors")
// but nothing prevents me from writing
getEntity<Author>("posts")

Есть ли хитрость, чтобы заставить это работать?

1 Ответ

0 голосов
/ 13 июня 2019

Не уверен насчет части MobX, но тип машинописного текста является довольно простым приложением запросов индексного типа и keyof:

class Author { id!: string; a!: string }
class Comment { id!: string; c!: string }
class Post {id!: string; p!: string }
class Store {
    entities = {
        authors: {} as Record<string, Author>,
        comments: {} as Record<string, Comment>,
        posts: {} as Record<string, Post>
    }
}
let store = new Store();
function addEntity<K extends keyof Store['entities']>(type: K, value: Store['entities'][K][string]) {
    let collection = store.entities[type] as Record<string, typeof value>; // some type assertions required
    collection[value.id] = value;
}
function getEntity<K extends keyof Store['entities']>(type: K, id: string): Store['entities'][K][string] {

    let collection = store.entities[type] as Record<string, Store['entities'][K][string]>; // some type assertions required
    return collection[id];
}
addEntity("authors", new Author()) 
addEntity("authors", new Post())  // err

// same here
let a: Author = getEntity("authors", "1") 
let p : Post = getEntity("authors", "1") //err 
...