mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
Test-case for TypeScript bug
This commit is contained in:
parent
5266c7c90f
commit
85beaccb0c
|
|
@ -1,2 +1,3 @@
|
|||
tournaments/lib/
|
||||
logs/
|
||||
dev-tools/globals.js
|
||||
|
|
|
|||
4
dev-tools/globals.ts
Normal file
4
dev-tools/globals.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
interface AnyObject {[k: string]: any}
|
||||
|
||||
var Dex = require('./sim/dex');
|
||||
// var Sim = require('./sim');
|
||||
115
sim/dex-data.js
115
sim/dex-data.js
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* Simulator Battle
|
||||
* Pokemon Showdown - http://pokemonshowdown.com/
|
||||
*
|
||||
* @license MIT license
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
class Tools {
|
||||
/**
|
||||
* Safely converts the passed variable into a string. Unlike '' + str,
|
||||
* String(str), or str.toString(), Dex.getString is guaranteed not to
|
||||
* crash.
|
||||
*
|
||||
* Specifically, the fear with untrusted JSON is an object like:
|
||||
*
|
||||
* let a = {"toString": "this is not a function"};
|
||||
* console.log(`a is ${a}`);
|
||||
*
|
||||
* This will crash (because a.toString() is not a function). Instead,
|
||||
* Dex.getString simply returns '' if the passed variable isn't a
|
||||
* string or a number.
|
||||
*
|
||||
* @param {any} str
|
||||
* @return {string}
|
||||
*/
|
||||
static getString(str) {
|
||||
if (typeof str === 'string' || typeof str === 'number') return '' + str;
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts anything to an ID. An ID must have only lowercase alphanumeric
|
||||
* characters.
|
||||
* If a string is passed, it will be converted to lowercase and
|
||||
* non-alphanumeric characters will be stripped.
|
||||
* If an object with an ID is passed, its ID will be returned.
|
||||
* Otherwise, an empty string will be returned.
|
||||
*
|
||||
* Dex.getId is generally assigned to the global toId, because of how
|
||||
* commonly it's used.
|
||||
*
|
||||
* @param {any} text
|
||||
* @return {string}
|
||||
*/
|
||||
static getId(text) {
|
||||
if (text && text.id) {
|
||||
text = text.id;
|
||||
} else if (text && text.userid) {
|
||||
text = text.userid;
|
||||
}
|
||||
if (typeof text !== 'string' && typeof text !== 'number') return '';
|
||||
return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '');
|
||||
}
|
||||
}
|
||||
const toId = Tools.getId;
|
||||
|
||||
class Effect {
|
||||
/**
|
||||
* @param {AnyObject} data
|
||||
*/
|
||||
constructor(data) {
|
||||
/**
|
||||
* ID. This will be a lowercase version of the name with all the
|
||||
* alphanumeric characters removed. So, for instance, "Mr. Mime"
|
||||
* becomes "mrmime", and "Basculin-Blue-Striped" becomes
|
||||
* "basculinbluestriped".
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
this.id = '';
|
||||
/**
|
||||
* Name. Currently does not support Unicode letters, so "Flabébé"
|
||||
* is "Flabebe" and "Nidoran♀" is "Nidoran-F".
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = '';
|
||||
/**
|
||||
* Full name. Prefixes the name with the effect type. For instance,
|
||||
* Leftovers would be "item: Leftovers", confusion the status
|
||||
* condition would be "confusion", etc.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
this.fullname = '';
|
||||
/**
|
||||
* Effect type.
|
||||
*
|
||||
* @type {'Effect' | 'Pokemon' | 'Move' | 'Item' | 'Ability'}
|
||||
*/
|
||||
this.effectType = 'Effect';
|
||||
/**
|
||||
* Does it exist? For historical reasons, when you use an accessor
|
||||
* for an effect that doesn't exist, you get a dummy effect that
|
||||
* doesn't do anything, and this field set to false.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.exists = false;
|
||||
|
||||
Object.assign(this, data);
|
||||
this.name = Tools.getString(data.name);
|
||||
this.fullname = Tools.getString(data.fullname) || this.name;
|
||||
this.id = toId(this.name);
|
||||
this.effectType = Tools.getString(data.effectType) || "Effect";
|
||||
this.exists = !!this.exists;
|
||||
}
|
||||
toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
exports.Tools = Tools;
|
||||
exports.Effect = Effect;
|
||||
70
sim/dex.js
70
sim/dex.js
|
|
@ -45,6 +45,8 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const DexData = require('./dex-data');
|
||||
|
||||
const DATA_DIR = path.resolve(__dirname, '../data');
|
||||
const MODS_DIR = path.resolve(__dirname, '../mods');
|
||||
const FORMATS = path.resolve(__dirname, '../config/formats');
|
||||
|
|
@ -77,14 +79,11 @@ if (!Object.values) {
|
|||
// };
|
||||
// }
|
||||
|
||||
/** @typedef {{[k: string]: any}} AnyObject */
|
||||
|
||||
/** @type {{[mod: string]: ModdedDex}} */
|
||||
let dexes = {};
|
||||
|
||||
/** @typedef {'Pokedex' | 'FormatsData' | 'Learnsets' | 'Movedex' | 'Statuses' | 'TypeChart' | 'Scripts' | 'Items' | 'Abilities' | 'Natures' | 'Formats' | 'Aliases'} DataType */
|
||||
/** @type {DataType[]} */
|
||||
// @ts-ignore TypeScript bug
|
||||
const DATA_TYPES = ['Pokedex', 'FormatsData', 'Learnsets', 'Movedex', 'Statuses', 'TypeChart', 'Scripts', 'Items', 'Abilities', 'Natures', 'Formats', 'Aliases'];
|
||||
|
||||
const DATA_FILES = {
|
||||
|
|
@ -135,20 +134,7 @@ const BattleNatures = {
|
|||
timid: {name:"Timid", plus:'spe', minus:'atk'},
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {any} text
|
||||
* @return {string}
|
||||
*/
|
||||
function toId(text) {
|
||||
// this is a duplicate of Dex.getId, for performance reasons
|
||||
if (text && text.id) {
|
||||
text = text.id;
|
||||
} else if (text && text.userid) {
|
||||
text = text.userid;
|
||||
}
|
||||
if (typeof text !== 'string' && typeof text !== 'number') return '';
|
||||
return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '');
|
||||
}
|
||||
const toId = DexData.Tools.getId;
|
||||
|
||||
class ModdedDex {
|
||||
|
||||
|
|
@ -170,6 +156,8 @@ class ModdedDex {
|
|||
this.formatsCache = null;
|
||||
this.modsLoaded = false;
|
||||
|
||||
this.getString = DexData.Tools.getString;
|
||||
this.getId = DexData.Tools.getId;
|
||||
this.ModdedDex = ModdedDex;
|
||||
}
|
||||
|
||||
|
|
@ -229,23 +217,6 @@ class ModdedDex {
|
|||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely converts the passed variable into a string. Unlike '' + str,
|
||||
* String(str), or str.toString(), Dex.getString is guaranteed not to
|
||||
* crash.
|
||||
*
|
||||
* The other methods of casting to string can crash if str.toString crashes
|
||||
* or isn't a function. Instead, Dex.getString simply returns '' if the
|
||||
* passed variable isn't a string or a number.
|
||||
*
|
||||
* @param {any} str
|
||||
* @return {string}
|
||||
*/
|
||||
getString(str) {
|
||||
if (typeof str === 'string' || typeof str === 'number') return '' + str;
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a username or Pokemon nickname
|
||||
*
|
||||
|
|
@ -282,30 +253,6 @@ class ModdedDex {
|
|||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts anything to an ID. An ID must have only lowercase alphanumeric
|
||||
* characters.
|
||||
* If a string is passed, it will be converted to lowercase and
|
||||
* non-alphanumeric characters will be stripped.
|
||||
* If an object with an ID is passed, its ID will be returned.
|
||||
* Otherwise, an empty string will be returned.
|
||||
*
|
||||
* Dex.getId is generally assigned to the global toId, because of how
|
||||
* commonly it's used.
|
||||
*
|
||||
* @param {any} text
|
||||
* @return {string}
|
||||
*/
|
||||
getId(text) {
|
||||
if (text && text.id) {
|
||||
text = text.id;
|
||||
} else if (text && text.userid) {
|
||||
text = text.userid;
|
||||
}
|
||||
if (typeof text !== 'string' && typeof text !== 'number') return '';
|
||||
return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* returns false if the target is immune; true otherwise
|
||||
*
|
||||
|
|
@ -589,8 +536,7 @@ class ModdedDex {
|
|||
let id = toId(name);
|
||||
let effect = {};
|
||||
if (id && this.data.Statuses[id]) {
|
||||
effect = this.data.Statuses[id];
|
||||
effect.name = effect.name || this.data.Statuses[id].name;
|
||||
effect = new DexData.Effect(this.data.Statuses[id]);
|
||||
} else if (id && this.data.Movedex[id] && this.data.Movedex[id].effect) {
|
||||
effect = this.data.Movedex[id].effect;
|
||||
effect.name = effect.name || this.data.Movedex[id].name;
|
||||
|
|
@ -1039,6 +985,7 @@ class ModdedDex {
|
|||
return false;
|
||||
}
|
||||
|
||||
/** @type {DataType[]} */
|
||||
searchIn = searchIn || ['Pokedex', 'Movedex', 'Abilities', 'Items', 'Natures'];
|
||||
|
||||
let searchFunctions = {Pokedex: 'getTemplate', Movedex: 'getMove', Abilities: 'getAbility', Items: 'getItem', Natures: 'getNature'};
|
||||
|
|
@ -1046,7 +993,7 @@ class ModdedDex {
|
|||
let searchResults = [];
|
||||
for (let i = 0; i < searchIn.length; i++) {
|
||||
/** @type {AnyObject} */
|
||||
// @ts-ignore TypeScript bug
|
||||
// @ts-ignore
|
||||
let res = this[searchFunctions[searchIn[i]]](target);
|
||||
if (res.exists) {
|
||||
searchResults.push({
|
||||
|
|
@ -1541,7 +1488,6 @@ class ModdedDex {
|
|||
dexes['base'].formatsCache[id] = format;
|
||||
if (this.dataCache) this.dataCache.Formats[id] = format;
|
||||
if (!this.isBase) {
|
||||
// @ts-ignore
|
||||
if (dexes['base'].dataCache) dexes['base'].dataCache.Formats[id] = format;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
},
|
||||
"types": ["node"],
|
||||
"include": [
|
||||
"./dev-tools/globals.ts",
|
||||
"./sim/dex.js",
|
||||
"./sim/dex-data.js",
|
||||
"./sim/prng.js"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user