Wait, which tetromino was that? Background
Tetris is a single-player game played on a rectangular grid with tetromino pieces.
When you fill one or more lines with tetrominoes, the filled lines are removed, and all blocks above them move down accordingly. In the following diagrams, . is an empty space, # is an existing block, and the tetromino marked with As is the one just placed:
One line cleared example

#...AA.. -&gt; ........
####AA##    #...##..

---

Two lines cleared example
(note that the 3rd line moved down once, while the top line moved twice)

...A....    ........
###A####    ........
##.A.##. -&gt; ...#....
###A####    ##.#.##.
####.###    ####.###

Challenge
Two board states will be given as input. One is right before a specific tetromino appears (the left-side state of the above diagrams, without As), and the other is right after the tetromino is placed and line clears are completed (the right-side state). Given this information, recover the type of the tetromino placed between the two states, which is one of the following seven types:
O   J      L   S    T    Z    I
##  #      #   ##   #   ##   ####
##  ###  ###  ##   ###   ##

You can assume the following:

The input is valid; the two boards have the same dimensions, and the game state can be changed from the first to the second by placing a single tetromino. Also, the placed tetromino is completely inside the grid before line clearing occurs (i.e. it won't be placed above the ceiling of the given grid, even partially).
The answer is unique.
The width of the board is at least 5.

For this challenge, ignore the rotation rules of actual Tetris games, and assume that any tetromino placement is valid, as long as the tetromino does not overlap with existing blocks or float in the air. This allows placing a tetromino inside a closed room (which actually happens in some exotic games).
You can take the input as a matrix (or equivalent) consisting of two distinct values for spaces and blocks. You can output the type of the tetromino as one of seven distinct values of your choice. Allowed output formats include numbers, strings, and possibly nested or multi-dimensional arrays of numbers and/or strings.
Standard code-golf rules apply. The shortest code in bytes wins.
Test cases
The notation is the same as the above example. Before and After are the two input grids, Answer is the expected output (given as alphabet code), and Explanation shows where the tetromino is placed.
Before:
......
......

After:
..##..
..##..

Answer: O
Explanation:
..AA..
..AA..

----------------

Before:
..####
...###
#.####

After:
......
......
..####

Answer: T
Explanation:
..####
AAA###
#A####

----------------

Before:
...###
...###
#.....

After:
......
..####
#.....

Answer: L (J doesn't work since the piece will be floating)
Explanation:
..A###
AAA###
#.....

----------------

Before:
##..##
##..##
##..##

After:
......
###.##
##.###

Answer: S
Explanation:
##A.##
##AA##
##.A##

----------------

Before:
##.##
##..#
##.##

After:
.....
.....
.....

Answer: T
Explanation: self-explanatory

----------------

Before:
.###.
#...#
.....
#...#
.###.

After:
.....
.###.
#...#
..#..
.###.

Answer: T
Explanation:
.###.
#...#
..A..
#AAA#
.###.
JavaScript (ES10),  286  282 bytes
Expects (a)(b), where \$a\$ and \$b\$ are 2 lists of binary strings. Returns an integer between \$0\$ and \$6\$ for JLTIOZS, or \$-1\$ if there's no solution (but we're not supposed to support that).
a=&gt;b=&gt;&quot;0NF71ZMA13FM000F1F1F1IFL2R8I38LT357N368H&quot;.match(/../g).findIndex(p=&gt;b.some((r,y)=&gt;[...r].some((_,x)=&gt;!b.some((s,i)=&gt;[...a.map(B=r=&gt;'0b'+r),~0].flatMap(t=(r,Y,a)=&gt;~(q=((parseInt(p,36)||4369)&gt;&gt;(Y-y)*4&amp;15)&lt;&lt;x,t|=q&amp;a[Y+1],q|=q&amp;r?~0:r)+2**s.length?q:(i--,[]))[i]^B(s)|!t))))%14&gt;&gt;1

Try it online!
How?
Shape encoding
Each shape is encoded with 2 digits in base 36, which allows to store:
$$\lfloor 2\times\log_2 36\rfloor=10\text{ bits}$$
The bits are arranged in a \$4\times 4\$ matrix, with the piece wedged in the upper right corner. The least significant bit is mapped to the top-right cell and there are 6 implicit leading zeros.
Examples:

The 1st entry is 0N in base 36, which is \$23\$ in decimal and \$0000010111\$ in binary. This is a J.

The 6th entry is FM in base 36, which is \$562\$ in decimal and \$1000110010\$ in binary. This is a T.



All shapes can be encoded that way, except the vertical I. This one is encoded as 00 and its actual value (\$4369\$ in decimal) is hard-coded separately.
Main algorithm
The main algorithm consists of 5 nested loops where we try to put each shape \$p\$ at each possible position \$(x,y)\$ in the grid \$a\$ and figure out which one leads to the grid \$b\$.
Whenever a row is completed, we yield an empty array so that .flatMap() deletes this row and we decrement the index \$i\$ of the entry that needs to be read in the resulting array to account for the row shift.
We may end up with a negative \$i\$. But because we use a XOR for the comparison, undefined rows behave like they were set to \$0\$, which is what we want.
&quot;...&quot;.match(/../g)              // list of shapes
.findIndex(p =&gt;                 // for each shape p:
  b.some((r, y) =&gt;              //   for each y:
    [...r].some((_, x) =&gt;       //     for each x:
      !b.some((s, i)=&gt;          //       for each row s at position i in b:
        [ ...a.map(             //         using the helper function B, decode a[] by
            B = r =&gt; '0b' + r   //         converting each binary string to an integer
          ), ~0                 //         and append a full line at the bottom
        ].flatMap(t =           //         initialize t to a non-numeric value
        (r, Y, a) =&gt;            //         for each row r at position Y in this array:
          ~(                    //
            q = (               //           q is the 4-bit mask of the shape
              ( parseInt(p, 36) //           decoded from the base-36 value p,
                || 4369 )       //
              &gt;&gt; (Y - y) * 4    //           keeping only the bits for this row
              &amp; 15              //
            ) &lt;&lt; x,             //           and left-shifting by x
            t |= q &amp; a[Y + 1],  //           update the 'touching' mask
            q |= q &amp; r ? ~0     //           invalidate the row if q overlaps r
                       : r      //           otherwise, merge q and r
          ) + 2 ** s.length ?   //           if the resulting line is not full:
            q                   //             yield q
          :                     //           else:
            (i--, [])           //             decrement i and yield an empty array,
                                //             which results in the deletion of this
                                //             line by flatMap()
        )[i]                    //         end of flatMap(); extract the i-th row
        ^ B(s)                  //         the test fails if this row is not equal to s
        | !t                    //         or the shape is floating in the air
      )                         //       end of some()
    )                           //     end of some()
  )                             //   end of some()
)                               // end of findIndex()

Final result
The shapes are stored in the following order:
0N F7 1Z MA 13 FM 00 0F 1F 1F 1I FL 2R 8I 38 LT 35 7N 36 8H
0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19
J  J  L  L  T  T  I  I  O  O  Z  Z  S  S  J  J  L  L  T  T
which is why the final result is the value returned by .findIndex() modulo \$14\$, divided by \$2\$ and rounded towards \$0\$.
(NB: The shape O is stored twice so that it doesn't break the pattern.)