Pocket Pokemon Type Evaluator
JavaScript (ES6), 270 246 221 bytes
Takes input in currying syntax (a)(d) where a is the attacking type as a string and d are the defending type(s) as an array of strings. The case doesn't matter.
Returns a standard multiplier as a float.

a=&gt;d=&gt;d.map(d=&gt;r*='0124'[&quot;ijfkj{jjnjjJ[kefjiZj[jxnjni^YnEW]vfeziiz]okjZmniV{oZvjhjnmj[zrfijij[yVzej[jjejjhujkmjZkZ[fjiuznjjz~zjfRjbkjy&quot;.charCodeAt((i=g(d)*18+g(a))/3)&gt;&gt;i%3*2&amp;3]/2,g=s=&gt;parseInt(s,36)*64%97%54%21%18,r=1)&amp;&amp;r

Try it online! or check the whole chart
How?
The base-36 interpretation \$N\$ of a given Pokemon type string is converted to a coordinate \$C\$ in \$[0..17]\$ using the following minimal perfect hash function:
$$C=((((N\times64)\bmod97)\bmod54)\bmod21)\bmod18$$
The chart is rearranged using the new coordinates and encoded as a single string with 6 bits of payload data per character, using ASCII codes in the range \$[64..127]\$.
There are 2 bits per cell, so the length of the string is \$(18\times18\times2)/6=108\$ characters.
For the curious, here is the code that encodes the string.