# Shapes: Subtyping

Consider two shape types having a common initial sequence of fields. For example:

```enum Bank: int {
DEPOSIT = 1;
// ...
}

type Transaction = shape('trtype' => Bank);
type Deposit = shape('trtype' => Bank, 'toaccnum' => int, 'amount' => float);
```

The shape type with the larger field set, `Deposit`, is a subtype of the one with the smaller field set, `Transaction`. The former has all the fields of the latter, so a value of the former can be used in place of the latter. For example, you can now write a function that operates on "all shapes that have a field called `'trtype'` having type `Bank`". For example:

```<?hh

namespace Hack\UserDocumentation\Shapes\Examples\Subtyping\Subtype;

enum Bank: int {
INVALID = 0;
DEPOSIT = 1;
WITHDRAWAL = 2;
TRANSFER = 3;
}

type Transaction = shape('trtype' => Bank);
type Deposit = shape('trtype' => Bank, 'toaccnum' => int, 'amount' => float);
type Withdrawal = shape('trtype' => Bank, 'fromaccnum' => int,
'amount' => float);
type Transfer = shape('trtype' => Bank, 'fromaccnum' => int,
'toaccnum' => int, 'amount' => float);

function processTransaction(Transaction \$t): void {
var_dump(\$t);

\$a = Shapes::toArray(\$t);
var_dump(count(\$a), \$a);

\$v = Shapes::idx(\$t, 'trtype', Bank::INVALID);  // checker accepts this
var_dump(\$v);

\$v = Shapes::keyExists(\$t, 'trtype');   // checker accepts this
var_dump(\$v);

Shapes::removeKey(\$t, 'xyz');   // checker accepts this
var_dump(\$t);

// checker complains Invalid argument (Typing[4140])
\$v = Shapes::idx(\$t, 'amount', -999.0); // The field 'amount' is missing
var_dump(\$v);

// checker complains Invalid argument (Typing[4140])
\$v = Shapes::keyExists(\$t, 'amount'); // The field 'amount' is missing
var_dump(\$v);

// checker is fine here because we used removeKey above
\$v = Shapes::keyExists(\$t, 'xyz'); // The field 'xyz' is missing
var_dump(\$v);

switch (\$t['trtype']) {
case Bank::TRANSFER:
echo "Transfer: " . \$t['amount'] . " from Account " . \$t['fromaccnum'] .
" to Account " . \$t['toaccnum'] . "\n";
break;
case Bank::DEPOSIT:
// The field amount is undefined (Typing[4108])
// The field toaccnum is undefined (Typing[4108])
echo "Deposit: " . \$t['amount'] . " to Account " . \$t['toaccnum'] . "\n";
break;
case Bank::WITHDRAWAL:
echo "Withdrawal: " . \$t['amount'] . " from Account " .
\$t['fromaccnum'] . "\n";
break;
default:
break;
}
}

function main(): void {
processTransaction(shape('trtype' => Bank::DEPOSIT, 'toaccnum' => 23456,
'amount' => 100.00));
processTransaction(shape('trtype' => Bank::WITHDRAWAL, 'fromaccnum' => 3157,
'amount' => 100.00));
processTransaction(shape('trtype' => Bank::TRANSFER, 'fromaccnum' => 23456,
'toaccnum' => 3157, 'amount' => 100.00));
}

main();
```
Output
```array(3) {
["trtype"]=>
int(1)
["toaccnum"]=>
int(23456)
["amount"]=>
float(100)
}
int(3)
array(3) {
["trtype"]=>
int(1)
["toaccnum"]=>
int(23456)
["amount"]=>
float(100)
}
int(1)
bool(true)
array(3) {
["trtype"]=>
int(1)
["toaccnum"]=>
int(23456)
["amount"]=>
float(100)
}
float(100)
bool(true)
bool(false)
Deposit: 100 to Account 23456
array(3) {
["trtype"]=>
int(2)
["fromaccnum"]=>
int(3157)
["amount"]=>
float(100)
}
int(3)
array(3) {
["trtype"]=>
int(2)
["fromaccnum"]=>
int(3157)
["amount"]=>
float(100)
}
int(2)
bool(true)
array(3) {
["trtype"]=>
int(2)
["fromaccnum"]=>
int(3157)
["amount"]=>
float(100)
}
float(100)
bool(true)
bool(false)
Withdrawal: 100 from Account 3157
array(4) {
["trtype"]=>
int(3)
["fromaccnum"]=>
int(23456)
["toaccnum"]=>
int(3157)
["amount"]=>
float(100)
}
int(4)
array(4) {
["trtype"]=>
int(3)
["fromaccnum"]=>
int(23456)
["toaccnum"]=>
int(3157)
["amount"]=>
float(100)
}
int(3)
bool(true)
array(4) {
["trtype"]=>
int(3)
["fromaccnum"]=>
int(23456)
["toaccnum"]=>
int(3157)
["amount"]=>
float(100)
}
float(100)
bool(true)
bool(false)
Transfer: 100 from Account 23456 to Account 3157
```

There is one important caveat, however. Inside function `processTransaction` the only field you can access in `\$t` is `'trtype'`. This is true even if you use a switch with `case Bank::DEPOSIT:`, for example, to determine the actual kind of the transaction.