typescript: Migrate dict.js to typescript.

This commit is contained in:
Thomas Ip
2019-02-08 18:56:33 +08:00
committed by Tim Abbott
parent a290333325
commit 7d050ab0cf
29 changed files with 194 additions and 149 deletions

View File

@@ -1,5 +1,4 @@
set_global('i18n', global.stub_i18n);
zrequire('dict');
zrequire('compose_state');
zrequire('ui_util');
zrequire('pm_conversations');

View File

@@ -1,4 +1,3 @@
zrequire('dict');
zrequire('user_pill');
zrequire('settings_user_groups');

View File

@@ -1,7 +1,6 @@
set_global('blueslip', global.make_zblueslip());
set_global('page_params', {});
zrequire('dict');
zrequire('user_groups');
run_test('user_groups', () => {

View File

@@ -27,7 +27,7 @@ global.window = _.extend({}, windowObj, {
},
});
global.Dict = require('js/dict');
global.Dict = require('ts/dict.ts').Dict;
// Create a helper function to avoid sneaky delays in tests.
function immediate(f) {

View File

@@ -7,6 +7,7 @@
"dependencies": {
"@types/node": "8.0.34",
"@types/webpack": "4.4.0",
"@types/underscore": "1.8.9",
"blueimp-md5": "2.10.0",
"clipboard": "2.0.1",
"css-hot-loader": "1.3.9",

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var activity = (function () {
var exports = {};

View File

@@ -37,7 +37,7 @@ import "js/search_util.js";
import "js/keydown_util.js";
import "js/lightbox_canvas.js";
import "js/rtl.js";
import "js/dict.js";
import "ts/dict.ts";
import "js/scroll_util.js";
import "js/components.js";
import "js/feedback_widget.js";

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var color_data = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var condense = (function () {
var exports = {};

View File

@@ -1,144 +0,0 @@
/* Constructs a new Dict object.
*
* This module primarily exists to support the fold_case option,
* because so many string keys in Zulip are case-insensitive (emails,
* stream names, topics, etc.).
*
* Dict(opt) -> the new Dict will be empty
*
* Available options:
* fold_case - Make has() and get() case-insensitive. keys() and
* other methods that implicitly return keys return the
* casing used for the most recent set()
*
*/
function Dict(opts) {
this._items = {};
this._opts = _.extend({}, {fold_case: false}, opts);
}
/* Constructs a new Dict object from another object.
*
* Dict.from(jsobj, opts) -> create a Dict with keys corresponding to the
* properties of jsobj and values corresponding to
* the value of the appropriate property. `opts` is
* passed to the Dict constructor.
*/
Dict.from = function Dict_from(obj, opts) {
if (typeof obj !== "object" || obj === null) {
throw new TypeError("Cannot convert argument to Dict");
}
var ret = new Dict(opts);
_.each(obj, function (val, key) {
ret.set(key, val);
});
return ret;
};
/* Like above, but constructs a Dict object from an array, setting the value of
* each element to `true`. Intended for use as a set. As above, `opts` is
* passed to the Dict constructor.
*/
Dict.from_array = function Dict_from_array(xs, opts) {
if (!(xs instanceof Array)) {
throw new TypeError("Argument is not an array");
}
var ret = new Dict(opts);
_.each(xs, function (x) {
ret.set(x, true);
});
return ret;
};
(function () {
Dict.prototype = {
_munge: function Dict__munge(k) {
if (k === undefined) {
blueslip.error("Tried to call a Dict method with an undefined key.");
return;
}
if (this._opts.fold_case) {
k = k.toLowerCase();
}
return ':' + k;
},
clone: function Dict_clone() {
var ret = new Dict(this._opts);
ret._items = _.clone(this._items);
return ret;
},
get: function Dict_get(key) {
var mapping = this._items[this._munge(key)];
if (mapping === undefined) {
return;
}
return mapping.v;
},
set: function Dict_set(key, value) {
this._items[this._munge(key)] = {k: key, v: value};
return value;
},
// If `key` exists in the Dict, return its value. Otherwise
// insert `key` with a value of `value` and return `value`.
setdefault: function Dict_setdefault(key, value) {
var mapping = this._items[this._munge(key)];
if (mapping === undefined) {
return this.set(key, value);
}
return mapping.v;
},
has: function Dict_has(key) {
return _.has(this._items, this._munge(key));
},
del: function Dict_del(key) {
return delete this._items[this._munge(key)];
},
keys: function Dict_keys() {
return _.pluck(_.values(this._items), 'k');
},
values: function Dict_values() {
return _.pluck(_.values(this._items), 'v');
},
items: function Dict_items() {
return _.map(_.values(this._items), function (mapping) {
return [mapping.k, mapping.v];
});
},
num_items: function Dict_num_items() {
return _.keys(this._items).length;
},
is_empty: function Dict_is_empty() {
return _.isEmpty(this._items);
},
// Iterates through the Dict calling f(value, key) for each (key, value) pair in the Dict
each: function Dict_each(f) {
return _.each(this._items, function (mapping) {
f(mapping.v, mapping.k);
});
},
clear: function Dict_clear() {
this._items = {};
},
};
}());
if (typeof module !== 'undefined') {
module.exports = Dict;
}
window.Dict = Dict;

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var muting = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var narrow_state = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var people = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var pm_conversations = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var reactions = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var recent_senders = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var settings_sections = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var settings_streams = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var starred_messages = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var stream_data = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var stream_list = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var topic_data = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var topic_list = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var typeahead_helper = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var typing_data = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
// See https://zulip.readthedocs.io/en/latest/subsystems/pointer.html for notes on
// how this system is designed.

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var user_groups = (function () {
var exports = {};

View File

@@ -1,3 +1,5 @@
var Dict = require('../ts/dict').Dict;
var user_status = (function () {
var exports = {};

149
static/ts/dict.ts Normal file
View File

@@ -0,0 +1,149 @@
import * as _ from 'underscore';
/**
* Implementation detail of the Dict class. `key` is `k` converted to a string,
* in lowercase if the `fold_case` option is enabled.
*/
type KeyValue<K, V> = { k: K, v: V }
type Items<K, V> = {
[key: string]: KeyValue<K, V>
}
/**
* This class primarily exists to support the fold_case option, because so many
* string keys in Zulip are case-insensitive (emails, stream names, topics,
* etc.). Dict also accepts any key that can be converted to a string.
*/
export class Dict<K, V> {
private _items: Items<K, V> = {};
private _fold_case: boolean;
/**
* @param opts - setting `fold_case` to true will make `has()` and `get()`
* case-insensitive. `keys()` and other methods that
* implicitly return keys return the original casing/type
* of the key passed into `set()`.
*/
constructor(opts?: {fold_case: boolean}) {
this._fold_case = opts ? opts.fold_case : false;
}
/**
* Constructs a Dict object from an existing object's keys and values.
* @param obj - A javascript object
* @param opts - Options to be passed to the Dict constructor
*/
static from<V>(obj: { [key: string]: V }, opts?: {fold_case: boolean}): Dict<string, V> {
if (typeof obj !== "object" || obj === null) {
throw new TypeError("Cannot convert argument to Dict");
}
let dict = new Dict<string, V>(opts);
for (const key in obj) {
dict.set(key, obj[key]);
}
return dict;
}
/**
* Construct a Dict object from an array with each element set to `true`.
* Intended for use as a set data structure.
* @param arr - An array of keys
* @param opts - Options to be passed to the Dict constructor
*/
static from_array<K, V>(arr: K[], opts?: {fold_case: boolean}): Dict<K, V | true> {
if (!(arr instanceof Array)) {
throw new TypeError("Argument is not an array");
}
let dict = new Dict<K, V | true>(opts);
for (const key of arr) {
dict.set(key, true);
}
return dict;
}
// Handle case-folding of keys and the empty string.
private _munge(key: K): string | undefined {
if (key === undefined) {
blueslip.error("Tried to call a Dict method with an undefined key.");
return undefined;
}
let str_key = ':' + key.toString();
return this._fold_case ? str_key.toLowerCase() : str_key;
}
clone(): Dict<K, V> {
let dict = new Dict<K, V>({fold_case: this._fold_case});
dict._items = { ...this._items };
return dict;
}
get(key: K): V | undefined {
const mapping = this._items[this._munge(key)];
if (mapping === undefined) {
return undefined;
}
return mapping.v;
}
set(key: K, value: V): V {
this._items[this._munge(key)] = {k: key, v: value};
return value;
}
/**
* If `key` exists in the Dict, return its value. Otherwise insert `key`
* with a value of `value` and return the value.
*/
setdefault(key: K, value: V): V {
const mapping = this._items[this._munge(key)];
if (mapping === undefined) {
return this.set(key, value);
}
return mapping.v;
}
has(key: K): boolean {
return _.has(this._items, this._munge(key));
}
del(key: K): boolean {
// `delete` could throw a TypeError in strict mode.
try {
delete this._items[this._munge(key)];
} catch (_e) {
return false;
}
return true;
}
keys(): K[] {
return _.pluck(_.values(this._items), 'k');
}
values(): V[] {
return _.pluck(_.values(this._items), 'v');
}
items(): [K, V][] {
return _.map(_.values(this._items),
(mapping: KeyValue<K, V>): [K, V] => [mapping.k, mapping.v]);
}
num_items(): number {
return _.keys(this._items).length;
}
is_empty(): boolean {
return _.isEmpty(this._items);
}
each(f: (v: V, k?: K) => void) {
_.each(this._items, (mapping: KeyValue<K, V>) => f(mapping.v, mapping.k));
}
clear() {
this._items = {};
}
}