Как объявить карту, содержащую определенные свойства, с помощью flowtype & immutable.js

С учетом объекта веб-сайта, созданного таким образом

import {Map} from 'immutable' 
const website = new Map({name: 'My Website', url: 'http://www.myw.fr'})

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

declare type websiteType = Map<string,string>

Но я хотел бы быть более конкретным и объявить карту, которая должна содержать свойства name и url типа string.

Возможно ли это?

Ответ 1

Надеюсь, я правильно понял ваш вопрос, потому что никогда не использовал карту из "неизменяемой", поэтому я буду использовать es6 Map.

Почему бы вам просто не использовать класс?

class Website extends Map<string, string> {
    constructor(name: string, url: string) {
        super()
        this.set("name", name)
        this.set("url", url)
    }
}

Таким образом вы можете инициализировать его следующим образом:

const website = new Website("awesome", "www.awesome.com")

а затем выполните операции get и set.

Если вы пропустите параметры, тип потока вызовет ошибку.

Надеюсь, это будет для вас решением.

EDIT:

Вы также можете создать функцию, которая инициализирует вашу карту.

declare type WebsiteType = Map<string, string>

function createWebsite(name: string, description: string) {
    const website: WebsiteType = new Map
    website.set("name", name)
    website.set("description", description)
    return website
}

Однако я считаю, что первое решение лучше, потому что оно дает вам тип веб-сайта и вам не нужно создавать функцию-создатель.

EDIT:

Если вам нужен такой же синтаксис, как вы использовали инициализацию карты, вы также можете сделать:

class Website extends Map<string, string> {
    constructor({name, url, ...rest}) {
        super()
        this.set("name", name)
        this.set("url", url)
        for(const name in rest) {
            this.set(name, rest[name])
        }
    }
}

Однако я считаю, что первый имеет смысл.

Ответ 2

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

type MyKeys = 'A' | 'B';
declare type MyMapType = Map<MyKeys, number>;

const TinyMap: MyMapType = Map({
  A: 1,
});
// As expected this doesn't give an error
console.log(TinyMap.get('B'));

const MyMap: MyMapType = Map({
  A: 1,
  B: 2,
});

// Just a 'number'
MyMap.get('A');

// Somewhat surprisingly this works fine
const NewMap = MyMap.set('A', 'a');

// Now of type 'number | string'
NewMap.get('A')

// Forcing the return into the type causes the error that one
// perhaps would expect: 'string This type is incompatible with number'
const AnotherMap: MyMapType = MyMap.set('A', 'a');

Ответ 3

Неизменяемый Map поддерживает getter/setters, поэтому вы можете рассматривать его точно как простой объект JS. Просто создайте тип с полями, которые необходимо ограничить:

type Website = {
  name: string,
  url: string,
}

Затем используйте его, где вам нужно:

import {Map} from 'immutable'

const website: Website = new Map({
  name: 'My Website',
  url: 'http://www.myw.fr'
})

website.name; // No error
website.url; // No error
website.somethingElse; // Error