g | x | w | all
Bytes Lang Time Link
786Python 3.8 prerelease250902T140134ZV_R
365JavaScript ES11250712T211227ZArnauld
356Ruby250713T004257ZLevel Ri
nan05AB1E250714T074652ZKevin Cr
205Charcoal250712T214044ZNeil

Python 3.8 (pre-release), 786 bytes

Just starting us off with a Python answer here, I'm sure you can do much better by copying an algorithm from another programming language's answer.

print(" "*7+"Def "+" ".join("NFWEGIFPGFPBRGDDSF")+"\nAtk"+" "*8+" ".join("oialrciorlsuohrata"))
for w in[('NFWEGIFPGFPBRGDDSFoialrciorlsuohratarrteaegioyygcoareimeecs hsuic ksgkera rts tonnh  to lyl  r  indgi   n      i  n   c          c  g'+" "*65+'8811881N888M85NNM8B83288S3GBBVGO33V88IHSLIN7M88QMO77NJEH3CDHDCBEE0ABCCAGE9UPY9C1M2EVXK00X5K2ZL521649KF4WHHKAS9WMU5DVRHSCOXQQPVBZEY03XB2VCJURMLBO3I5QVN72GL9YWGS6QO5EJUZ4WOT4SV2ER4RKGCR0ZKIG7I6TCT6R5CR6M030GVJM0D3RK934HN850YXJBTC9Q5C6FYGTTWUGGQMCYIXPH9BVEK41VPCVH3EKGQJ7RY1E3BYNYZM18IC64H34JWW05TXEUFVFRWSSML0GMSGYIT48HW9PK68XIAEM7P8G9JESKNS5XV6M  URG7MPET9QPNX3E1  EAT5XII42 ULTI      K9   Y  SI1       E'+' '*11)[x::18].strip()for x in range(18)]:print(w[:11]+"".join([{"0":"-","1":" ","2":"X","3":"+"}[x]for x in str(int(w[11:],36))]))

The data table (r[11:] for every row r) is encoded as a base-36 number. Each row is padded with spaces until they're all the same length (34 characters). This allows the list of rows to be stored slightly more efficiently, as a string which is then unpacked. The base-36 numbers are converted back to base 10, and an inline-defined dictionary converts the digits to the symbols - X+.

Try it online!

JavaScript (ES11), 365 bytes

A function that returns an array of strings.

_=>[..."       Def0Atk1Normal1dwFire2jka7goWater4gxrs5cElectric152vkn4Grass2zolphgIce2jaogp0Fighting0fstk089Poison28ovf2Ground4836scoFlying6sq8zoPsychic693joBug21c7ajpRock3yoxsskGhost0noddde8Dragon3rDark2im1dSteel2ls6sxyFairy1z6qbvo".matchAll(/(\D+)([^A-Z]+)/g)].map(([,s,q],_,[,,...a])=>s.padEnd(11)+a.map(a=>a[1][q]||" -+X"[parseInt(q,36)/4**--i&3],i=18).join` `)

Attempt This Online!

Encoding

Each row is encoded as:

All encoded rows are joined together into a single data string without explicit separators:

      +---------------------> label
      |     +---------------> base-36 value
      |     |     +---------> label
      |     |     |     +---> base-36 value
    __|_  __|_  __|_  __|__
   /    \/    \/    \/     \
...Poison28ovf2Ground4836sco...

The fields are extracted with the following regular expression:

/(\D+)([^A-Z]+)/g

The transitions value → label are triggered by a letter in upper case.

The transitions label → value are triggered by a digit. Most values start with a digit but we occasionally have to force a leading zero, as in:

Fighting0fstk089Poison

Commented

_ =>                    // input is ignored
[                       //
  ..."[..]Fairy1z6qbvo" // data string
  .matchAll(            // extract all fields from it,
    /(\D+)([^A-Z]+)/g   // using this regular expression
  )                     // (as described in the previous section)
].map((                 // for each pair ...
  [, s, q],             // ... [ s, q ] = [ label, value ]
  _,                    // (the index is ignored)
  [,, ...a]             // with a[] = array without the first 2 entries:
) =>                    //
  s.padEnd(11) +        //   append the right-padded label
  a.map(a =>            //   for each entry a[] in a[]:
    a[1][q]             //     attempt to extract the 1st or 2nd letter
                        //     for the abbreviated label at the top
    ||                  //     if undefined (q is not number-ish),
    " -+X"[             //     using this lookup string for types:
      parseInt(q, 36)   //       convert q from base-36 to decimal
      / 4 ** --i        //       decrement i and divide by 4 ** i
      & 3               //       isolate the 2 least significant bits
    ],                  //     end of lookup
    i = 18              //     start with i = 18
  ).join` `             //   end of inner map(); join with spaces
)                       // end of outer map()

Ruby, 356 bytes

->{q=["%-13dDef"%6]+%w{7pppppAtk
jjjjdbNormal
B~jzHnFire
NbzjKjWater
z`ZkJjElectric
rbrHKbGrass
BNzkzbIce
kzb@gOFighting
jnBj`vPoison
ncnIknGround
jlkzhbFlying
jjobjaPsychic
bn`lbCBug
nzH;jbRock
ijjnnhGhost
jjjjzRDragon
jjhnnHDark
BxjjkrSteel
bjcjzcFairy}
q.map{|i|k=i.to_i
"%-11s"%i[6,10]+(0..17).map{|j|k>1?q[j+2][k]:((i[j/3].ord>>j%3*2&3)+47).chr}*' '}}

Try it online!

A function returning an array of strings. Outputs /012 for -X + A major rewrite of my original answer.

The first 6 characters of each line of q contain the information for the data table. 2 bits are extracted giving a number 0..3 to which we add 47 and convert to ASCII, to give a character in the range /012.

The first two rows work slightly differently:Where the line begins with a number, this is evaluated and the character q[j+2][k] is displayed, thereby deriving the column labels from the row labels.

05AB1E, 169 (or 168) bytes

.•1ˆL$ζ•™”Atk©–å¼—™Ä¬Ôììí¶°ä ÿ𳉫·ä±Þ–°–ÍÞš”#…Def7úšíTjíD¦¦εS2£}ø•@µš‘ŒìqgÏAδy…ëò¥ìΔΘ˜‡!,RƒTÿΩÆ£iÇßBÃ-иÊ™¹нjïð+ÈHγΘÕ79ÔÙê¾βÙ-γ_ñβת+ñ`×ΘE@ÇõÀ«²b@±Ö•4в18ô3'½:«‚ø€˜»

Uses 0½12 for X- + (immune; resist; neutral; super effective) respectively.
: Could be -1 byte by using a space character instead of ½ by replacing with ð, but ½ is more suitable, so each number is its damage multiplier.

Try it online or try it online with pretty-printed X- + output.

Explanation:

.•1ˆL$ζ•     # Push compressed string "psychic"
        ™    # Titlecase it to "Psychic"
”Atk©–å¼—™Ä¬Ôììí¶°ä ÿ𳉫·ä±Þ–°–ÍÞš”
             # Push dictionary string "Atk Normal Fire Water Electric Grass Ice Fighting Poison Ground Flying ÿ Bug Rock Ghost Dragon Dark Steel Fairy",
             # where the `ÿ` is replaced with the "Psychic"
  #          # Split it on spaces
   …Def      # Push "Def"
       7ú    # Prepend 7 spaces to it
         š   # Prepend this string to the list
í            # Reverse each string in the list
 Tj          # Pad leading spaces to make each string of length 10
   í         # Reverse each string back again
D            # Duplicate the list
 ¦¦          # Remove the two leading item ("       Def" and "Atk       ")
   ε         # Map over all other strings
    S        #  Convert it to a list of characters
     2£      #  Only keep the first 2
   }ø        # After the map: zip/transpose; swapping rows/columns
•@µš‘ŒìqgÏAδy…ëò¥ìΔΘ˜‡!,RƒTÿΩÆ£iÇßBÃ-иÊ™¹нjïð+ÈHγΘÕ79ÔÙê¾βÙ-γ_ñβת+ñ`×ΘE@ÇõÀ«²b@±Ö•
             # Push compressed integer 389328296633318817330695706216782159888569961083708007911368710102238024154759501461517770063911646412871652185459105369034601776247500363278801175447442354566730014261843141772393055451354584493
  4в         # Convert it to base-4 as list:
             #  [1,1,1,1,1,1,1,1,1,1,1,1,3,0,1,1,3,1,1,3,3,1,2,2,1,1,1,1,1,2,3,1,3,1,2,1,1,2,3,1,3,1,1,1,2,1,1,1,2,1,3,1,1,1,1,1,2,3,3,1,1,1,0,2,1,1,1,1,3,1,1,1,1,3,2,1,3,1,1,3,2,3,1,3,2,1,3,1,3,1,1,3,3,1,2,3,1,1,2,2,1,1,1,1,2,1,3,1,2,1,1,1,1,2,1,3,1,3,3,3,2,0,1,2,2,3,1,1,1,1,2,1,1,3,3,1,1,1,3,3,1,1,0,2,1,2,1,2,3,1,1,2,1,0,1,3,2,1,1,1,2,1,1,1,1,3,2,1,2,1,1,1,1,2,3,1,1,1,3,1,1,1,1,1,1,1,2,2,1,1,3,1,1,1,1,0,3,1,1,3,1,1,2,1,3,3,1,3,2,1,1,3,1,2,3,3,1,2,1,1,1,2,3,1,3,2,1,2,1,1,1,1,3,1,0,1,1,1,1,1,1,1,1,1,2,1,1,2,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,3,0,1,1,1,1,1,1,3,1,1,1,2,1,1,2,1,3,1,3,1,3,3,3,1,2,1,1,1,1,1,1,2,1,1,1,3,2,1,3,1,1,1,1,2,3,1,1,1,1,1,1,2,2,3,1]
    18ô      # Split it into blocks of size 18 each
       3'½: '# Replace all "3"s with "½"s
«            # Merge this list to the pair of character-lists
 ‚           # Pair it with the list of types
  ø          # Zip/transpose; swapping rows/columns
   €˜        # Flatten each inner pair of type + row to a single row
     »       # Join each inner list by spaces,
             # and then this list of strings by newlines
             # (after which the result is output implicitly)

See this 05AB1E tip of mine (all four sections) to understand why .•1ˆL$ζ• is "psychic"; ”Atk©–å¼—™Ä¬Ôììí¶°ä ÿ𳉫·ä±Þ–°–ÍÞš” is "Atk Normal Fire Water Electric Grass Ice Fighting Poison Ground Flying ÿ Bug Rock Ghost Dragon Dark Steel Fairy"; •@µš...@±Ö• is 389...493; and •@µš...@±Ö•4в is [1,1,1,...,2,3,1].

Charcoal, 205 bytes

≔E⪪”↶±↗ι7%2]BKpη✂@≧W⁹DAE↓α⪫⊙M⍘∕φ↨Lβ➙✂+⌕c|7lLq.‴u↧χε⊞/\`²=Yj·xEDX◨Y.¦×)℅FΣ×;⊖Z” ⭆ι⎇μλ↥λθP↑⪫Eθ⮌…ι²¶↘ P”}∧✂-ηι$δS⊕V℅;⦄e↶rP⬤″X⁺β⁹↗u↶D←w?�≡§\`αφf﹪⪫÷jυTTηsNYⅉ´+χ~∨wÀαK⭆l↙§εGε‖´J]⦄7μ➙∕>O´À¹⬤8υ➙ρ▶↓λ?c”↖↑UE¹←feD↓M⁶←θ

Try it online! Link is to verbose version of code. Explanation:

≔E⪪”...” ⭆ι⎇μλ↥λθ

Get a list of the 19 left-hand labels in lower case (because it compresses better), and then title-case them.

P↑⪫Eθ⮌…ι²¶

Output the first two letters of each label reversed upwards i.e. downwards but in the desired order.

Overwrite the t of At of the first "label" and move to the type chart area itself.

P”...”

Output the type chart as a compressed string.

↖↑

Move to the A of At.

UE¹

Space everything out horizontally.

←feD

Overwrite the A of Atk with Def backwards leftwards.

↓M⁶←θ

Move to the left-hand label position and print them.