TypeScript |不变的扩展Immutable.Map类型的正确方法

时间:2017-04-25 10:07:40

标签: typescript immutable.js

我有一个使用immutable包在typescript中编写的react-redux应用程序。我有一个来自api和商店的数据我把它打包到Map。在所有应用程序中,它们都用作Map。

我创建了一个界面:

export interface PaymentMethod extends Immutable.Map<string, string | NamedType<number>> {
    id: string;
    name: string;
    description: string;
    accountNr: string;
    paymentMethodType: NamedType<number>;
}

一般来说,它的效果非常好。除了测试,我以这种方式创建数据:

const dummyPaymentMethod: PaymentMethod = Map({
    id: '',
    name: '',
    description: '',
    accountNr: '',
    paymentMethodType: { id: 1, name: '' },
});

然后我得到一个lint错误:

Error:(116, 13) TS2322:Type 'Map<string, string | { id: number; name: string; }>' is not assignable to type 'PaymentMethod'.
Property 'id' is missing in type 'Map<string, string | { id: number; name: string; }>'.

我觉得完全迷失了,因为我可以在界面和虚拟数据中看到id。

我会很感激它。我觉得我应该把可接受的密钥列表传递给我的Map,但不知道怎么做。

编辑:拼错

2 个答案:

答案 0 :(得分:6)

我们在项目中使用它(略有不同的方法):

interface ImmutableMap<T> extends Map<string, any> {
  get<K extends keyof T>(name: K): T[K];
}

我们使用了尚未使用映射类型的Immutable.js类型的旧版本(T[K])。 AFAIK类型从那时起更新,无需覆盖get方法。

编辑:实际上get方法仍然不是完全类型安全的,不像上面那样。因此,覆盖该方法仍然有其优点。

通过上述声明,您可以创建不可变的地图,如:

type AuthState = ImmutableMap<{
  user:string|null;
  loggedIn:boolean;
}>;

const authState:AuthState = fromJS({ user: 'Alice', loggedIn: true });

理想情况下,您希望这样的打字:

/**
 * Imaging these are typings for your favorite immutable
 * library. We used it to enhance typings of `immutable.js`
 * with the latest TypeScript features.
 */
declare function Immutable<T>(o: T): Immutable<T>;
interface Immutable<T> {
  get<K extends keyof T>(name: K): T[K];
  set<S>(o: S): Immutable<T & S>;
}

const alice = Immutable({ name: 'Alice', age: 29 });
alice.get('name');      // Ok, returns a `string`
alice.get('age');       // Ok, returns a `number`
alice.get('lastName');  // Error: Argument of type '"lastName"' is not assignable to parameter of type '"name" | "age"'.

const aliceSmith = alice.set({ lastName: 'Smith' });
aliceSmith.get('name');     // Ok, returns a `string`
aliceSmith.get('age');      // Ok, returns a `number`
aliceSmith.get('lastName'); // Ok, returns `string`

Link to the Playground

为了通过Immutable.js实现上述功能,您可以创建一个小帮助函数,其唯一目的是“修复”类型:

import { fromJS } from 'immutable';

interface Immutable<T> {
  get<K extends keyof T>(name: K): T[K];
  set<S>(o: S): Immutable<T & S>;
}

function createImmutable<T extends object> (o:T) {
  return fromJS(o) as Immutable<T>;
}

请注意,我在示例中使用了fromJS。只要传递的输入为Map,这将创建Object。使用fromJS优于Map的好处是,打字更容易被覆盖。

附注:您可能还想查看Record s。

答案 1 :(得分:0)

留在这里以防它对某人有帮助:

import { Map } from 'immutable';

export interface ImmutableMap<JsObject extends object> extends Omit<Map<keyof JsObject, JsObject>, 'set' | 'get' | 'delete'> {
    set: <AKeyOfThatJsObject extends keyof JsObject>(key: AKeyOfThatJsObject, value: ValueOf<Pick<JsObject, AKeyOfThatJsObject>>) => ImmuMap<JsObject>;
    get: <AKeyOfThatJsObject extends keyof JsObject>(key: AKeyOfThatJsObject) => ValueOf<Pick<JsObject, AKeyOfThatJsObject>>;
    delete: <AKeyOfThatJsObject extends keyof JsObject>(key: AKeyOfThatJsObject, value: ValueOf<Pick<JsObject, AKeyOfThatJsObject>>) => ImmuMap<JsObject>;
}

这对我们来说真的很棒!

我们这样使用它:const aMap: ImmutableMap<OneOfOurObjectTypes>

示例用法:

type BarType = { bar: string };

const foo: BarType = {
    bar: 'abc';
};
const immutableBar: Immutable<BarType> = Map(foo);
// immutableBar.get suggests bar as the key, and knows that the result is a string.