Tìm kiếm trang web

typescript - kiểu tham số từ điển chuỗi hoặc số


Tôi có hàm này:

interface NumDict<T> {
  [key : number] : T
}

export function mapNumDictValues<T,R>(dict: NumDict<T>, f: (v: T, key?: number) => R): NumDict<R> {
  let emptyDict : NumDict<R> = {};
  return Object.keys(dict).reduce((acc, key) => {
    const keyInt = parseInt(key);
    acc[keyInt] = f(dict[keyInt], keyInt);
    return acc;
  }, emptyDict);
}

Bây giờ tôi cũng muốn nó hoạt động cho từ điển được đánh chỉ số bằng chuỗi cũng như từ điển được đánh chỉ số bằng số, ví dụ như một cái gì đó như:

function mapDictValues<K extends string|number,T,R>(obj: {[id: K]: T}, f: (v: T, key?: K) => R): {[id: K]: R} {

Tuy nhiên, điều này làm tôi nhận được lỗi này:

error TS1023: An index signature parameter type must be 'string' or 'number'.

Có cách nào không?

Câu trả lời #1

Thử cái này:

interface IStringToNumberDictionary {
    [index: string]: number;
}


interface INumberToStringDictionary {
    [index: number]: string;
}

type IDictionary = IStringToNumberDictionary | INumberToStringDictionary;

Ví dụ :

let dict: IDictionary = Object.assign({ 0: 'first' }, { 'first': 0 });
let numberValue = dict["first"]; // 0
let stringValue = dict[0]; // first

Trong trường hợp của bạn, điều gì đó như thế này:

interface IStringKeyDictionary<T> {
    [index: string]: T;
}


interface INumberKeyDictionary<T> {
    [index: number]: T;
}

type IDictionary<T> = IStringKeyDictionary<T> | INumberKeyDictionary<T>;

Câu trả lời #2

Những gì bạn đang tìm kiếm không dễ thực hiện (ít nhất là tôi không thể tìm ra cách) vì cách JavaScript xử lý các khóa đối tượng (chỉ chuỗi) và các hạn chế của TypeScript về biểu thức chỉ mục (chuỗi/số/ký hiệu/bất kỳ) và sự khác biệt giữa các kiểu number | string và number/string.

Loại an toàn duy nhất mà bạn có được (theo những gì tôi hiểu) là bạn nhận được lỗi khi sử dụng noImplicitAny (lỗi khó hiểu) cho một trường hợp, nhưng bạn vẫn bị hạn chế với những gì bạn có thể làm với từ điển này.

Tôi không biết nhiều về trường hợp của bạn, nhưng nghe có vẻ bạn có ít lợi ích ở đây để có được giải pháp hợp lý, trừ khi bạn thực sự cần tính an toàn về kiểu, thì tôi nghĩ tốt hơn là chỉ nên coi các khóa của bạn như chuỗi và giải quyết cho xong, nhưng nếu bạn cần điều đó thì tôi gợi ý bạn tạo chính các triển khai kiểu từ điển của riêng mình để xử lý, ví dụ:

interface Dict<K extends number | string, V> {
    get(key: K): V;
    set(key: K, value: V): void;
    mapValues<R>(f: (v: V, key?: K) => R): Dict<K, R>;
}

abstract class BaseDict<K extends number | string, V> implements Dict<K, V> {
    protected items: { [key: string]: V } = Object.create(null);

    get(key: K): V {
        return this.items[key.toString()];
    }

    set(key: K, value: V): void {
        this.items[key.toString()] = value;
    }

    abstract keys(): K[];

    values(): V[] {
        return Object.keys(this.items).map(key => this.items[key]);
        // or Object.values(this.items) with ES6
    }

    mapValues<R>(fn: (v: V, key?: K) => R): Dict<K, R> {
        let dict: Dict<K, R> = Object.create(this.constructor.prototype);
        this.keys().forEach(key => dict.set(key, fn(this.get(key), key)));
        return dict;
    }
}

class NumDict<V> extends BaseDict<number, V> {
    keys(): number[] {
        return Object.keys(this.items).map(key => parseFloat(key));
    }
}

class StringDict<V> extends BaseDict<string, V> {
    keys(): string[] {
        return Object.keys(this.items);
    }
}

Bạn sẽ cần tạo các dict của mình với các ctors, điều này không thoải mái như việc sử dụng {}, nhưng bạn sẽ có nhiều quyền kiểm soát hơn về mọi thứ, ví dụ hãy chú ý rằng khi gọi hàm map (fn) trong BaseDict.mapValues thì key sẽ có kiểu đúng (number trong trường hợp NumDict và string cho StringDict).

Bài viết liên quan