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.
This commit is contained in:
Guangcong Luo
2019-10-12 05:51:40 +11:00
parent f32f63a3f0
commit 0d05feb41b
13 changed files with 91 additions and 99 deletions

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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 += `<span class="picon" style="` + Dex.getPokemonIcon('pokeball-none') + `"></span>`;
} else if (noShow && poke && poke.fainted) {
} else if (noShow && poke?.fainted) {
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball-fainted') + `" title="Fainted" aria-label="Fainted"></span>`;
} else if (noShow && poke && poke.status) {
} else if (noShow && poke?.status) {
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball-statused') + `" title="Status" aria-label="Status"></span>`;
} else if (noShow) {
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball') + `" title="Non-statused" aria-label="Non-statused"></span>`;
@@ -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, {

View File

@@ -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;

View File

@@ -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 = `<small>${BattleLog.escapeHTML(group)}</small><span class="username" data-name="${BattleLog.escapeHTML(name)}">${BattleLog.escapeHTML(name)}</span>`;
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);

View File

@@ -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]);

View File

@@ -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);

View File

@@ -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 += '<h2>' + name + genderBuf + (pokemon.level !== 100 ? ' <small>L' + pokemon.level + '</small>' : '') + '<br />';
if (clientPokemon && clientPokemon.volatiles.formechange) {
if (clientPokemon?.volatiles.formechange) {
if (clientPokemon.volatiles.transform) {
text += '<small>(Transformed into ' + clientPokemon.volatiles.formechange[1] + ')</small><br />';
} else {
@@ -672,7 +672,7 @@ class BattleTooltips {
}
let itemText = '';
if (serverPokemon && serverPokemon.item) {
if (serverPokemon?.item) {
itemText = '<small>Item:</small> ' + 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 = '&#8226; ' + 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 + '<br />';
}
text += '</p>';
} else if (!this.battle.hardcoreMode && clientPokemon && clientPokemon.moveTrack.length) {
} else if (!this.battle.hardcoreMode && clientPokemon?.moveTrack.length) {
// move list (guessed)
text += '<p class="section">';
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) {

View File

@@ -392,7 +392,7 @@ class Pokemon implements PokemonDetails, PokemonHealth {
return '' + badBoostTable[-this.boosts[boostStat]] + '&nbsp;' + 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));
}

View File

@@ -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);
}

View File

@@ -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<Event>}> {
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 <button class="select teamselect preselected" name="team" value="random" disabled>
<div class="team">

View File

@@ -46,15 +46,15 @@ class PSHeader extends preact.Component<{style: {}}> {
formatid = idChunks[idChunks.length - 2];
}
if (!title) {
let battle = (room as any).battle;
let p1 = (battle && battle.p1 && battle.p1.name) || '';
let p2 = (battle && battle.p2 && battle.p2.name) || '';
let battle = (room as any).battle as Battle | undefined;
let p1 = battle?.p1?.name || '';
let p2 = battle?.p2?.name || '';
if (p1 && p2) {
title = '' + p1 + ' v. ' + p2;
title = `${p1} v. ${p2}`;
} else if (p1 || p2) {
title = '' + p1 + p2;
title = `${p1}${p2}`;
} else {
title = '(empty room)';
title = `(empty room)`;
}
}
icon = <i class="text">{formatid}</i>;

View File

@@ -165,7 +165,7 @@ class PSMain extends preact.Component {
window.addEventListener('click', e => {
let elem = e.target as HTMLElement | null;
if (elem && elem.className === 'ps-overlay') {
if (elem?.className === 'ps-overlay') {
PS.closePopup();
e.preventDefault();
e.stopImmediatePropagation();