From 0d05feb41bd0760281e4e4d75ccc8285d9ad1054 Mon Sep 17 00:00:00 2001
From: Guangcong Luo
Date: Sat, 12 Oct 2019 05:51:40 +1100
Subject: [PATCH] Migrate to optional chaining and TypeScript 3.7
This is something I probably wouldn't have rushed nearly as much,
except I have a serious use-case coming up; specifically
```js
foo = document.querySelector('foo')?.bar;
```
in a class property.
`document.querySelector('foo') && document.querySelector('foo').bar`
is incredibly inefficient here, but TypeScript doesn't consider
`(document.querySelector('foo') || {}).bar` to be valid because `{}`
doesn't have the `.bar` property. Creating a temporary variable
would have been a huge mess because this was a class property
initializer.
In hindsight, `(document.querySelector('foo')! || {}).bar` would
probably have worked, but too late now.
---
.babelrc | 1 +
package.json | 3 ++-
src/battle-animations.ts | 12 +++++-----
src/battle-dex.ts | 47 +++++++++++++++++++--------------------
src/battle-log.ts | 27 +++++++++++-----------
src/battle-search.ts | 6 ++---
src/battle-text-parser.ts | 8 +++----
src/battle-tooltips.ts | 30 ++++++++++++-------------
src/battle.ts | 20 ++++++++---------
src/client-main.ts | 9 +++-----
src/panel-mainmenu.tsx | 13 +++++------
src/panel-topbar.tsx | 12 +++++-----
src/panels.tsx | 2 +-
13 files changed, 91 insertions(+), 99 deletions(-)
diff --git a/.babelrc b/.babelrc
index 20f9821dc..27ee33e22 100644
--- a/.babelrc
+++ b/.babelrc
@@ -5,6 +5,7 @@
["@babel/plugin-proposal-class-properties", {"loose": true}],
+ ["@babel/plugin-proposal-optional-chaining", {"loose": true}],
["@babel/plugin-proposal-object-rest-spread", {"loose": true, "useBuiltIns": true}],
"@babel/plugin-proposal-optional-catch-binding",
diff --git a/package.json b/package.json
index 24fc6151c..67fb1cec4 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
"@babel/cli": "^7.2.3",
"@babel/core": "^7.3.3",
"@babel/plugin-proposal-class-properties": "^7.3.3",
+ "@babel/plugin-proposal-optional-chaining": "^7.6.0",
"@babel/plugin-transform-member-expression-literals": "^7.2.0",
"@babel/plugin-transform-property-literals": "^7.2.0",
"@babel/plugin-transform-react-jsx": "^7.3.0",
@@ -33,7 +34,7 @@
"mocha": "^6.0.2",
"preact": "^8.3.1",
"tslint": "^5.13.0",
- "typescript": "^3.4.5"
+ "typescript": "^3.7.0-dev.20191011"
},
"private": true
}
diff --git a/src/battle-animations.ts b/src/battle-animations.ts
index 02a27c344..23c0a43db 100644
--- a/src/battle-animations.ts
+++ b/src/battle-animations.ts
@@ -557,7 +557,7 @@ class BattleScene {
if (Dex.prefs('nopastgens')) gen = 6;
if (Dex.prefs('bwgfx') && gen > 5) gen = 5;
this.gen = gen;
- this.activeCount = this.battle.mySide && this.battle.mySide.active.length || 1;
+ this.activeCount = this.battle.mySide?.active.length || 1;
const isSPL = (typeof this.battle.rated === 'string' && this.battle.rated.startsWith("Smogon Premier League"));
let bg: string;
@@ -584,7 +584,7 @@ class BattleScene {
}
getDetailsText(pokemon: Pokemon) {
- let name = pokemon.side && pokemon.side.n &&
+ let name = pokemon.side?.n &&
(this.battle.ignoreOpponent || this.battle.ignoreNicks) ? pokemon.species : pokemon.name;
if (name !== pokemon.species) {
name += ' (' + pokemon.species + ')';
@@ -618,9 +618,9 @@ class BattleScene {
let poke = side.pokemon[i];
if (i >= side.totalPokemon && i >= side.pokemon.length) {
pokemonhtml += ``;
- } else if (noShow && poke && poke.fainted) {
+ } else if (noShow && poke?.fainted) {
pokemonhtml += ``;
- } else if (noShow && poke && poke.status) {
+ } else if (noShow && poke?.status) {
pokemonhtml += ``;
} else if (noShow) {
pokemonhtml += ``;
@@ -1322,9 +1322,7 @@ class BattleScene {
return pokemon.sprite.afterMove();
}
updateSpritesForSide(side: Side) {
- if (side.missedPokemon && side.missedPokemon.sprite) {
- side.missedPokemon.sprite.destroy();
- }
+ side.missedPokemon?.sprite?.destroy();
side.missedPokemon = {
sprite: new PokemonSprite(null, {
diff --git a/src/battle-dex.ts b/src/battle-dex.ts
index 0ca2cc8ce..9b875fb31 100644
--- a/src/battle-dex.ts
+++ b/src/battle-dex.ts
@@ -133,9 +133,9 @@ function getString(str: any) {
}
function toID(text: any) {
- if (text && text.id) {
+ if (text?.id) {
text = text.id;
- } else if (text && text.userid) {
+ } else if (text?.userid) {
text = text.userid;
}
if (typeof text !== 'string' && typeof text !== 'number') return '' as ID;
@@ -220,16 +220,16 @@ const Dex = new class implements ModdedDex {
resourcePrefix = (() => {
let prefix = '';
- if (!window.document || !document.location || document.location.protocol !== 'http:') prefix = 'https:';
- return prefix + '//play.pokemonshowdown.com/';
+ if (window.document?.location?.protocol !== 'http:') prefix = 'https:';
+ return `${prefix}//play.pokemonshowdown.com/`;
})();
fxPrefix = (() => {
- if (window.document && document.location && document.location.protocol === 'file:') {
- if (window.Replays) return 'https://play.pokemonshowdown.com/fx/';
- return 'fx/';
+ if (window.document?.location?.protocol === 'file:') {
+ if (window.Replays) return `https://play.pokemonshowdown.com/fx/`;
+ return `fx/`;
}
- return '//play.pokemonshowdown.com/fx/';
+ return `//play.pokemonshowdown.com/fx/`;
})();
loadedSpriteData = {xy: 1, bw: 0};
@@ -255,7 +255,7 @@ const Dex = new class implements ModdedDex {
if (avatar.charAt(0) === '#') {
return Dex.resourcePrefix + 'sprites/trainers-custom/' + toID(avatar.substr(1)) + '.png';
}
- if (avatar.includes('.') && window.Config && Config.server && Config.server.registered) {
+ if (avatar.includes('.') && window.Config?.server?.registered) {
// custom avatar served by the server
let protocol = (Config.server.port === 443) ? 'https' : 'http';
return protocol + '://' + Config.server.host + ':' + Config.server.port +
@@ -284,8 +284,7 @@ const Dex = new class implements ModdedDex {
prefs(prop: string, value?: any, save?: boolean) {
// @ts-ignore
- if (window.Storage && Storage.prefs) return Storage.prefs(prop, value, save);
- return undefined;
+ return window.Storage?.prefs?.(prop, value, save);
}
getShortName(name: string) {
@@ -661,9 +660,9 @@ const Dex = new class implements ModdedDex {
getPokemonIconNum(id: ID, isFemale?: boolean, facingLeft?: boolean) {
let num = 0;
- if (window.BattlePokemonSprites && BattlePokemonSprites[id] && BattlePokemonSprites[id].num) {
+ if (window.BattlePokemonSprites?.[id]?.num) {
num = BattlePokemonSprites[id].num;
- } else if (window.BattlePokedex && window.BattlePokedex[id] && BattlePokedex[id].num) {
+ } else if (window.BattlePokedex?.[id]?.num) {
num = BattlePokedex[id].num;
}
if (num < 0) num = 0;
@@ -688,26 +687,26 @@ const Dex = new class implements ModdedDex {
getPokemonIcon(pokemon: any, facingLeft?: boolean) {
if (pokemon === 'pokeball') {
- return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -0px 4px';
+ return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -0px 4px`;
} else if (pokemon === 'pokeball-statused') {
- return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -40px 4px';
+ return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -40px 4px`;
} else if (pokemon === 'pokeball-fainted') {
- return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -80px 4px;opacity:.4;filter:contrast(0)';
+ return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -80px 4px;opacity:.4;filter:contrast(0)`;
} else if (pokemon === 'pokeball-none') {
- return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -80px 4px';
+ return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -80px 4px`;
}
let id = toID(pokemon);
- if (pokemon && pokemon.species) id = toID(pokemon.species);
- if (pokemon && pokemon.volatiles && pokemon.volatiles.formechange && !pokemon.volatiles.transform) {
+ if (pokemon?.species) id = toID(pokemon.species);
+ if (pokemon?.volatiles?.formechange && !pokemon.volatiles.transform) {
id = toID(pokemon.volatiles.formechange[1]);
}
- let num = this.getPokemonIconNum(id, pokemon && pokemon.gender === 'F', facingLeft);
+ let num = this.getPokemonIconNum(id, pokemon?.gender === 'F', facingLeft);
let top = Math.floor(num / 12) * 30;
let left = (num % 12) * 40;
- let fainted = (pokemon && pokemon.fainted ? ';opacity:.3;filter:grayscale(100%) brightness(.5)' : '');
- return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-sheet.png?a6) no-repeat scroll -' + left + 'px -' + top + 'px' + fainted;
+ let fainted = (pokemon?.fainted ? `;opacity:.3;filter:grayscale(100%) brightness(.5)` : ``);
+ return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-sheet.png?a6) no-repeat scroll -${left}px -${top}px${fainted}`;
}
getTeambuilderSprite(pokemon: any, gen: number = 0) {
@@ -723,7 +722,7 @@ const Dex = new class implements ModdedDex {
}
let shiny = (pokemon.shiny ? '-shiny' : '');
// let sdata;
- // if (BattlePokemonSprites[id] && BattlePokemonSprites[id].front && !Dex.prefs('bwgfx')) {
+ // if (BattlePokemonSprites[id]?.front && !Dex.prefs('bwgfx')) {
// if (BattlePokemonSprites[id].front.anif && pokemon.gender === 'F') {
// spriteid += '-f';
// sdata = BattlePokemonSprites[id].front.anif;
@@ -757,7 +756,7 @@ const Dex = new class implements ModdedDex {
getItemIcon(item: any) {
let num = 0;
if (typeof item === 'string' && exports.BattleItems) item = exports.BattleItems[toID(item)];
- if (item && item.spritenum) num = item.spritenum;
+ if (item?.spritenum) num = item.spritenum;
let top = Math.floor(num / 16) * 24;
let left = (num % 16) * 24;
diff --git a/src/battle-log.ts b/src/battle-log.ts
index 55c4673ed..e4086c4ee 100644
--- a/src/battle-log.ts
+++ b/src/battle-log.ts
@@ -63,13 +63,13 @@ class BattleLog {
this.elem.onscroll = null;
}
add(args: Args, kwArgs?: KWArgs, preempt?: boolean) {
- if (kwArgs && kwArgs.silent) return;
+ if (kwArgs?.silent) return;
let divClass = 'chat';
let divHTML = '';
let noNotify: boolean | undefined;
switch (args[0]) {
case 'chat': case 'c': case 'c:':
- let battle = this.scene && this.scene.battle;
+ let battle = this.scene?.battle;
let name;
let message;
if (args[0] === 'c:') {
@@ -80,12 +80,12 @@ class BattleLog {
message = args[2];
}
let rank = name.charAt(0);
- if (battle && battle.ignoreSpects && ' +'.includes(rank)) return;
- if (battle && battle.ignoreOpponent) {
+ if (battle?.ignoreSpects && ' +'.includes(rank)) return;
+ if (battle?.ignoreOpponent) {
if ('\u2605\u2606'.includes(rank) && toUserid(name) !== app.user.get('userid')) return;
}
- if (window.app && app.ignore && app.ignore[toUserid(name)] && ' +\u2605\u2606'.includes(rank)) return;
- let isHighlighted = window.app && app.rooms && app.rooms[battle!.roomid].getHighlight(message);
+ if (window.app?.ignore?.[toUserid(name)] && ' +\u2605\u2606'.includes(rank)) return;
+ let isHighlighted = window.app?.rooms?.[battle!.roomid].getHighlight(message);
[divClass, divHTML, noNotify] = this.parseChatMessage(message, name, '', isHighlighted);
if (!noNotify && isHighlighted) {
let notifyTitle = "Mentioned by " + name + " in " + battle!.roomid;
@@ -403,7 +403,7 @@ class BattleLog {
static usernameColor(name: ID) {
if (this.colorCache[name]) return this.colorCache[name];
let hash;
- if (window.Config && Config.customcolors && Config.customcolors[name]) {
+ if (window.Config?.customcolors?.[name]) {
hash = MD5(Config.customcolors[name]);
} else {
hash = MD5(name);
@@ -460,7 +460,7 @@ class BattleLog {
static prefs(name: string) {
// @ts-ignore
- if (window.Storage && Storage.prefs) return Storage.prefs(name);
+ if (window.Storage?.prefs) return Storage.prefs(name);
// @ts-ignore
if (window.PS) return PS.prefs[name];
return undefined;
@@ -469,7 +469,7 @@ class BattleLog {
parseChatMessage(
message: string, name: string, timestamp: string, isHighlighted?: boolean
): [string, string, boolean?] {
- let showMe = !(BattleLog.prefs('chatformatting') || {}).hideme;
+ let showMe = !BattleLog.prefs('chatformatting')?.hideme;
let group = ' ';
if (!/[A-Za-z0-9]/.test(name.charAt(0))) {
// Backwards compatibility
@@ -479,8 +479,7 @@ class BattleLog {
const colorStyle = ` style="color:${BattleLog.usernameColor(toID(name))}"`;
const clickableName = `${BattleLog.escapeHTML(group)}${BattleLog.escapeHTML(name)}`;
let hlClass = isHighlighted ? ' highlighted' : '';
- let isMine = (window.app && app.user && app.user.get('name') === name) ||
- (window.PS && PS.user.name === name);
+ let isMine = (window.app?.user?.get('name') === name) || (window.PS?.user.name === name);
let mineClass = isMine ? ' mine' : '';
let cmd = '';
@@ -589,7 +588,7 @@ class BattleLog {
}
static interstice = (() => {
- const whitelist: string[] = (window.Config && Config.whitelist) ? Config.whitelist : [];
+ const whitelist: string[] = window.Config?.whitelist || [];
const patterns = whitelist.map(entry => new RegExp(
`^(https?:)?//([A-Za-z0-9-]*\\.)?${entry}(/.*)?`,
'i'));
@@ -610,7 +609,7 @@ class BattleLog {
};
})();
- static tagPolicy: (tagName: string, attribs: string[]) => any = null!;
+ static tagPolicy: ((tagName: string, attribs: string[]) => any) | null = null;
static initSanitizeHTML() {
if (this.tagPolicy) return;
if (!('html4' in window)) {
@@ -755,7 +754,7 @@ class BattleLog {
let formattedTime;
// Try using Intl API if it exists
- if (window.Intl && Intl.DateTimeFormat) {
+ if (window.Intl?.DateTimeFormat) {
formattedTime = new Intl.DateTimeFormat(undefined, {
month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric',
}).format(parsedTime);
diff --git a/src/battle-search.ts b/src/battle-search.ts
index 63bf94aae..ff675d30c 100644
--- a/src/battle-search.ts
+++ b/src/battle-search.ts
@@ -732,7 +732,7 @@ class BattleSearch {
let uselessMoves: SearchRow[] = [];
let sketchedMoves: SearchRow[] = [];
for (const id of moves) {
- let isViable: boolean = BattleMovedex[id] && BattleMovedex[id].isViable;
+ let isViable: boolean = BattleMovedex[id]?.isViable;
if (id === 'aerialace') isViable = ['scyther', 'aerodactylmega', 'kricketune'].includes(toID(set.species));
if (id === 'ancientpower') {
isViable = (
@@ -813,7 +813,7 @@ class BattleSearch {
'acidarmor', 'amnesia', 'barrier', 'bind', 'clamp', 'confuseray', 'counter', 'firespin',
'hyperbeam', 'mirrormove', 'pinmissile', 'razorleaf', 'sing', 'slash', 'sludge',
'twineedle', 'wrap',
- ].includes) {
+ ].includes(id)) {
isViable = true;
}
@@ -904,7 +904,7 @@ class BattleSearch {
let poorAbilities: SearchRow[] = [['header', "Situational Abilities"]];
let badAbilities: SearchRow[] = [['header', "Unviable Abilities"]];
for (const id of abilities) {
- let rating = BattleAbilities[id] && BattleAbilities[id].rating;
+ let rating = BattleAbilities[id]?.rating;
if (id === 'normalize') rating = 3;
if (rating >= 3) {
goodAbilities.push(['ability', id]);
diff --git a/src/battle-text-parser.ts b/src/battle-text-parser.ts
index 8cdb5cca4..ccc0303b0 100644
--- a/src/battle-text-parser.ts
+++ b/src/battle-text-parser.ts
@@ -521,7 +521,7 @@ class BattleTextParser {
if (kwArgs.block) templateId = 'block';
if (kwArgs.upkeep) templateId = 'upkeep';
if (id === 'reflect' || id === 'lightscreen') templateId = 'startGen1';
- if (templateId === 'start' && kwArgs.from && kwArgs.from.startsWith('item:')) {
+ if (templateId === 'start' && kwArgs.from?.startsWith('item:')) {
templateId += 'FromItem';
}
const template = this.template(templateId, effect);
@@ -538,7 +538,7 @@ class BattleTextParser {
}
let templateId = 'end';
let template = '';
- if (kwArgs.from && kwArgs.from.startsWith('item:')) {
+ if (kwArgs.from?.startsWith('item:')) {
template = this.template('endFromItem', effect);
}
if (!template) template = this.template(templateId, effect);
@@ -654,7 +654,7 @@ class BattleTextParser {
return template.replace('[POKEMON]', this.pokemon(pokemon));
}
const line1 = this.maybeAbility(kwArgs.from, kwArgs.of || pokemon);
- if (kwArgs.from && kwArgs.from.startsWith('item:')) {
+ if (kwArgs.from?.startsWith('item:')) {
const template = this.template('endFromItem', status);
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(kwArgs.from));
}
@@ -859,7 +859,7 @@ class BattleTextParser {
else if (amount === 0) templateId += '0';
if (amount && kwArgs.zeffect) {
templateId += (kwArgs.multiple ? 'MultipleFromZEffect' : 'FromZEffect');
- } else if (amount && kwArgs.from && kwArgs.from.startsWith('item:')) {
+ } else if (amount && kwArgs.from?.startsWith('item:')) {
templateId += 'FromItem';
}
const template = this.template(templateId, kwArgs.from);
diff --git a/src/battle-tooltips.ts b/src/battle-tooltips.ts
index d0fb6c3e9..1c2fbeba4 100644
--- a/src/battle-tooltips.ts
+++ b/src/battle-tooltips.ts
@@ -26,7 +26,7 @@ class ModifiableValue {
this.serverPokemon = serverPokemon;
this.itemName = Dex.getItem(serverPokemon.item).name;
- const ability = serverPokemon.ability || (pokemon && pokemon.ability) || serverPokemon.baseAbility;
+ const ability = serverPokemon.ability || pokemon?.ability || serverPokemon.baseAbility;
this.abilityName = Dex.getAbility(ability).name;
this.weatherName = Dex.getMove(battle.weather).exists ?
Dex.getMove(battle.weather).name : Dex.getAbility(battle.weather).name;
@@ -43,7 +43,7 @@ class ModifiableValue {
this.comment.push(` (${itemName} suppressed by Magic Room)`);
return false;
}
- if (this.pokemon && this.pokemon.volatiles['embargo']) {
+ if (this.pokemon?.volatiles['embargo']) {
this.comment.push(` (${itemName} suppressed by Embargo)`);
return false;
}
@@ -58,7 +58,7 @@ class ModifiableValue {
}
tryAbility(abilityName: string) {
if (abilityName !== this.abilityName) return false;
- if (this.pokemon && this.pokemon.volatiles['gastroacid']) {
+ if (this.pokemon?.volatiles['gastroacid']) {
this.comment.push(` (${abilityName} suppressed by Gastro Acid)`);
return false;
}
@@ -620,7 +620,7 @@ class BattleTooltips {
text += '' + name + genderBuf + (pokemon.level !== 100 ? ' L' + pokemon.level + '' : '') + '
';
- if (clientPokemon && clientPokemon.volatiles.formechange) {
+ if (clientPokemon?.volatiles.formechange) {
if (clientPokemon.volatiles.transform) {
text += '(Transformed into ' + clientPokemon.volatiles.formechange[1] + ')
';
} else {
@@ -672,7 +672,7 @@ class BattleTooltips {
}
let itemText = '';
- if (serverPokemon && serverPokemon.item) {
+ if (serverPokemon?.item) {
itemText = 'Item: ' + Dex.getItem(serverPokemon.item).name;
} else if (clientPokemon) {
let item = '';
@@ -706,7 +706,7 @@ class BattleTooltips {
for (const moveid of serverPokemon.moves) {
let move = Dex.getMove(moveid);
let moveName = '• ' + move.name;
- if (battlePokemon && battlePokemon.moveTrack) {
+ if (battlePokemon?.moveTrack) {
for (const row of battlePokemon.moveTrack) {
if (moveName === row[0]) {
moveName = this.getPPUseText(row, true);
@@ -717,7 +717,7 @@ class BattleTooltips {
text += moveName + '
';
}
text += '
';
- } else if (!this.battle.hardcoreMode && clientPokemon && clientPokemon.moveTrack.length) {
+ } else if (!this.battle.hardcoreMode && clientPokemon?.moveTrack.length) {
// move list (guessed)
text += '';
for (const row of clientPokemon.moveTrack) {
@@ -739,7 +739,7 @@ class BattleTooltips {
calculateModifiedStats(clientPokemon: Pokemon | null, serverPokemon: ServerPokemon) {
let stats = {...serverPokemon.stats};
let pokemon = clientPokemon || serverPokemon;
- const isPowerTrick = clientPokemon && clientPokemon.volatiles['powertrick'];
+ const isPowerTrick = clientPokemon?.volatiles['powertrick'];
for (const statName of Dex.statNamesExceptHP) {
let sourceStatName = statName;
if (isPowerTrick) {
@@ -857,7 +857,7 @@ class BattleTooltips {
if (ability === 'solarpower') {
stats.spa = Math.floor(stats.spa * 1.5);
}
- let allyActive = clientPokemon && clientPokemon.side.active;
+ let allyActive = clientPokemon?.side.active;
if (allyActive) {
for (const ally of allyActive) {
if (!ally || ally.fainted) continue;
@@ -955,7 +955,7 @@ class BattleTooltips {
}
renderStats(clientPokemon: Pokemon | null, serverPokemon?: ServerPokemon | null, short?: boolean) {
- const isTransformed = clientPokemon && clientPokemon.volatiles.transform;
+ const isTransformed = clientPokemon?.volatiles.transform;
if (!serverPokemon || isTransformed) {
if (!clientPokemon) throw new Error('Must pass either clientPokemon or serverPokemon');
let [min, max] = this.getSpeedRange(clientPokemon);
@@ -1164,7 +1164,7 @@ class BattleTooltips {
}
return value;
}
- if (pokemon && pokemon.boosts.accuracy) {
+ if (pokemon?.boosts.accuracy) {
if (pokemon.boosts.accuracy > 0) {
value.modify((pokemon.boosts.accuracy + 3) / 3);
} else {
@@ -1238,7 +1238,7 @@ class BattleTooltips {
else basePower = 20;
value.set(basePower);
}
- if (move.id === 'hex' && target && target.status) {
+ if (move.id === 'hex' && target?.status) {
value.modify(2, 'Hex + status');
}
if (move.id === 'punishment' && target) {
@@ -1582,7 +1582,7 @@ class BattleTooltips {
abilityData.baseAbility = clientPokemon.baseAbility;
}
} else {
- const species = clientPokemon.getSpecies() || (serverPokemon && serverPokemon.species) || '';
+ const species = clientPokemon.getSpecies() || serverPokemon?.species || '';
const template = this.battle.dex.getTemplate(species);
if (template.exists && template.abilities) {
abilityData.possibilities = [template.abilities['0']];
@@ -2242,9 +2242,9 @@ class BattleStatGuesser {
}
if (natureOverride) {
val *= natureOverride;
- } else if (BattleNatures[set.nature!] && BattleNatures[set.nature!].plus === stat) {
+ } else if (BattleNatures[set.nature!]?.plus === stat) {
val *= 1.1;
- } else if (BattleNatures[set.nature!] && BattleNatures[set.nature!].minus === stat) {
+ } else if (BattleNatures[set.nature!]?.minus === stat) {
val *= 0.9;
}
if (!this.supportsEVs) {
diff --git a/src/battle.ts b/src/battle.ts
index 725334614..133bd96ce 100644
--- a/src/battle.ts
+++ b/src/battle.ts
@@ -392,7 +392,7 @@ class Pokemon implements PokemonDetails, PokemonHealth {
return '' + badBoostTable[-this.boosts[boostStat]] + ' ' + boostStatTable[boostStat];
}
getWeightKg(serverPokemon?: ServerPokemon) {
- let autotomizeFactor = this.volatiles.autotomize ? this.volatiles.autotomize[1] * 100 : 0;
+ let autotomizeFactor = this.volatiles.autotomize?.[1] * 100 || 0;
return Math.max(this.getTemplate(serverPokemon).weightkg - autotomizeFactor, 0.1);
}
getBoostType(boostStat: BoostStatName) {
@@ -486,7 +486,7 @@ class Pokemon implements PokemonDetails, PokemonHealth {
}
let item = toID(serverPokemon ? serverPokemon.item : this.item);
- let ability = toID(this.ability || (serverPokemon && serverPokemon.ability));
+ let ability = toID(this.ability || serverPokemon?.ability);
if (battle.hasPseudoWeather('Magic Room') || this.volatiles['embargo'] || ability === 'klutz') {
item = '' as ID;
}
@@ -779,8 +779,8 @@ class Side {
pokemon.clearVolatile();
pokemon.lastMove = '';
this.battle.lastMove = 'switch-in';
- if (this.lastPokemon && (this.lastPokemon.lastMove === 'batonpass' || this.lastPokemon.lastMove === 'zbatonpass')) {
- pokemon.copyVolatileFrom(this.lastPokemon);
+ if (['batonpass', 'zbatonpass'].includes(this.lastPokemon?.lastMove!)) {
+ pokemon.copyVolatileFrom(this.lastPokemon!);
}
this.battle.scene.animSummon(pokemon, slot);
@@ -1271,7 +1271,7 @@ class Battle {
updateToxicTurns() {
for (const side of this.sides) {
for (const poke of side.active) {
- if (poke && poke.status === 'tox') poke.statusData.toxicTurns++;
+ if (poke?.status === 'tox') poke.statusData.toxicTurns++;
}
}
}
@@ -1783,7 +1783,7 @@ class Battle {
let poke = this.getPokemon(args[1]);
if (poke) {
this.scene.resultAnim(poke, 'Super-effective', 'bad');
- if (window.Config && Config.server && Config.server.afd) {
+ if (window.Config?.server?.afd) {
this.scene.runOtherAnim('hitmark' as ID, [poke]);
}
}
@@ -2969,7 +2969,7 @@ class Battle {
// search p1's pokemon
if (siden !== this.p2.n && !isNew) {
const active = this.p1.active[slot];
- if (active && active.searchid === searchid && !isSwitch) {
+ if (active?.searchid === searchid && !isSwitch) {
active.slot = slot;
return active;
}
@@ -2996,7 +2996,7 @@ class Battle {
// search p2's pokemon
if (siden !== this.p1.n && !isNew) {
const active = this.p2.active[slot];
- if (active && active.searchid === searchid && !isSwitch) {
+ if (active?.searchid === searchid && !isSwitch) {
if (slot >= 0) active.slot = slot;
return active;
}
@@ -3188,7 +3188,7 @@ class Battle {
return;
} else if (args[1].slice(-14) === ' seconds left.') {
let hasIndex = args[1].indexOf(' has ');
- let userid = (window.app && app!.user && app!.user.get('userid'));
+ let userid = window.app?.user?.get('userid');
if (toID(args[1].slice(0, hasIndex)) === userid) {
this.kickingInactive = parseInt(args[1].slice(hasIndex + 5), 10) || true;
}
@@ -3407,7 +3407,7 @@ class Battle {
let nextArgs: Args = [''];
let nextKwargs: KWArgs = {};
const nextLine = this.activityQueue[this.activityStep + 1] || '';
- if (nextLine && nextLine.substr(0, 2) === '|-') {
+ if (nextLine.slice(0, 2) === '|-') {
({args: nextArgs, kwArgs: nextKwargs} = BattleTextParser.parseLine(nextLine));
}
diff --git a/src/client-main.ts b/src/client-main.ts
index 431839325..fe9140f43 100644
--- a/src/client-main.ts
+++ b/src/client-main.ts
@@ -159,10 +159,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
const formatid = toID(format);
if (!window.BattleFormats) return formatid;
const formatEntry = BattleFormats[formatid];
- if (formatEntry && formatEntry.teambuilderFormat) {
- return formatEntry.teambuilderFormat;
- }
- return formatid;
+ return formatEntry?.teambuilderFormat || formatid;
}
getKey(team: Team | null) {
if (!team) return '';
@@ -209,7 +206,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
let slashIndex = line.lastIndexOf('/', pipeIndex);
if (slashIndex < 0) slashIndex = bracketIndex; // line.slice(slashIndex + 1, pipeIndex) will be ''
let format = bracketIndex > 0 ? line.slice(0, bracketIndex) : 'gen7';
- if (format && format.slice(0, 3) !== 'gen') format = 'gen6' + format;
+ if (format.slice(0, 3) !== 'gen') format = 'gen6' + format;
return {
name: line.slice(slashIndex + 1, pipeIndex),
format: format as ID,
@@ -786,7 +783,7 @@ const PS = new class extends PSModel {
}
const roomType = this.roomTypes[options.type];
- if (roomType && roomType.title) options.title = roomType.title;
+ if (roomType?.title) options.title = roomType.title;
const Model = roomType ? roomType.Model : PlaceholderRoom;
return new Model(options);
}
diff --git a/src/panel-mainmenu.tsx b/src/panel-mainmenu.tsx
index 81e6f2039..db19ea846 100644
--- a/src/panel-mainmenu.tsx
+++ b/src/panel-mainmenu.tsx
@@ -73,11 +73,11 @@ class MainMenuRoom extends PSRoom {
if (!room.pmTarget) continue;
const targetUserid = toID(room.pmTarget);
if (!room.challengedFormat && !(targetUserid in json.challengesFrom) &&
- !room.challengingFormat && (json.challengeTo || {}).to !== targetUserid) {
+ !room.challengingFormat && json.challengeTo?.to !== targetUserid) {
continue;
}
room.challengedFormat = json.challengesFrom[targetUserid] || null;
- room.challengingFormat = (json.challengeTo || {}).to === targetUserid ? json.challengeTo.format : null;
+ room.challengingFormat = json.challengeTo?.to === targetUserid ? json.challengeTo.format : null;
room.update('');
}
}
@@ -164,7 +164,7 @@ class MainMenuRoom extends PSRoom {
isTeambuilderFormat = false;
}
}
- if (BattleFormats[id] && BattleFormats[id].isTeambuilderFormat) {
+ if (BattleFormats[id]?.isTeambuilderFormat) {
isTeambuilderFormat = true;
}
// make sure formats aren't out-of-order
@@ -332,10 +332,7 @@ class MainMenuPanel extends PSRoomPanel {
class FormatDropdown extends preact.Component<{format?: string, onChange?: JSX.EventHandler}> {
base?: HTMLButtonElement;
getFormat() {
- if (this.base && this.base.value) {
- return this.base.value;
- }
- return '[Gen 7] Random Battle';
+ return this.base?.value || '[Gen 7] Random Battle';
}
componentDidMount() {
this.base!.value = this.getFormat();
@@ -379,7 +376,7 @@ class TeamDropdown extends preact.Component<{format: string}> {
change = () => this.forceUpdate();
render() {
const formatid = PS.teams.teambuilderFormat(this.props.format);
- const formatData = window.BattleFormats && BattleFormats[formatid];
+ const formatData = window.BattleFormats?.[formatid];
if (formatData && formatData.team) {
return