With keyword typeof, keyof, infer, … We can extend and derive almost any types from existing types.
In specific use cases, there will be some cusomized types to resovle problem:
React.js
Typescript helps gooding props typing for React.
PropType<T, K>
Given the props type of a component, get the type of one specific property. (It helps a lot if we require this property in the upper level component)
/** Given a object type, and a property name, get the type of that object property
* Example:
* type Foo = { a: number, b: { x: string[], y: (y: string) => boolean }, c: () => void }
* type A = PropType<Foo, 'a'> // number
* type B = PropType<Foo, 'b'> // { x: string[], y: (y: string) => boolean }
* type C = PropType<Foo, 'c'> // () => void
* type Bx = PropType<PropType<Foo, 'b'>, 'x'> // string[]
* type By = PropType<PropType<Foo, 'b'>, 'y'> // (y: string) => boolean
*/exporttypePropType<TObj,TPropextendskeyofTObj>=TObj[TProp]
ReactFCPropsType<T, K>
Givent a third party React component library, we may suffer that the lib does not export the type of the component props but we do need it.
Defining types in code for 3rd party props may suffer compatibility problem when library updates its interface. We can just infer the type and make it easy to find the type mismatch when compiling.
/** Given a type of React function component, get the type of the props of it.
* It could be used if some 3rd party component does not externally export the props type, but we need it.
* (Use `typeof` to assign the type of React.FC component as generic parameter)
* Example:
* ```
* import { Tree, Button } from 'antd'
*
* type TreeProps = ReactFCPropsType<typeof Tree> // The type of the props required by Tree
* type ButtonProps = ReactFCPropsType<typeof Button> // The type of the props required by Button
* ```
*/exporttypeReactFCPropsType<TReactFC>=TReactFCextends(props:inferU,...args:any[])=>any?U:never
Answer
```java
public static void f() {
String[] a = new String[2];
Object[] b = a;
a[0] = "hi";
b[1] = Integer.valueOf(42); // <--- Runtime exception: java.lang.ArrayStoreException
}
```
Why not like this?
```java
public static void f() {
String[] a = new String[2];
Object[] b = a; // ~~~~~~~ Compile error: "a": String[] could not be assigned to "b": Object[]
a[0] = "hi";
b[1] = Integer.valueOf(42);
}
```
It is Object array here, why Object is right?
```java
public static void f() {
String a = new String();
Object b = a; // Typical polymorphism
a = "hi";
b = Integer.valueOf(42); // Awesome!
}
```
What's the difference?
- Array has multiple values?
- `b[0]` and `b[1]` cannot have different types?
- We say "String[] should not be assigned to Object[]", why "assign String to Object" is the core of OOP?
Answer
- In OOP, we say String is a typically subtype of Object.
- Assign subtype to super-type is always correct.
- ***But, WATCH OUT! String[] is not the subtype of Object[] !***
In conclusion, we could say T[A] is subtype of T[B], regardless T’s definition but only know A is subtype of B.
2. Covariant, contravariant, bivariant and invariant
In TypeScript, we use keyword type and generic to construct new types from existing, which is called Type Constructor.
e.g. type C<T> = T[]
It happens at compile time only, which totally disappears at JavaScript runtime.
We say type C defines a collection mapping from T to C<T>.
The subtype between such type mapping or constrution C is called “Variance”.
Given collection A is subtype/subset of B, we say A ⊆ B in math:
Variance is the relationshipt between mapped collections C<A> and C<B>
In math, we use ⊆, ⊇ to represent the subset/superset relationship. In CS, we use <: for subtyping.
Covariance
It keeps the subset relationship.
A <: B => C<A> <: C<B>
Contravariance
It reverses the relationship. The original child set is constructed to be superset.
A <: B => C<A> :> C<B>
Biariance
Both relationship applied.
A <: B => C<A> <: C<B> AND C<A> :> C<B>
Invariance
No variance anymore after remap.
A <: B => C<A> ⊄ C<B> AND C<B> ⊄ C<A>
3. Covariance in arrays
Back to the question above, the problem comes to String[] is not subtype of Object[]?
Covariance, contravariance, invariance?
In Java, we see it is covariance. String[] is allowed to be assigned to Object[].
But obviously, it’s not correct when write the array is writtable.
If we say the array is read only, covariance is correct now.
/* readonly*/ Object[] a = new String[] {"foo", "bar", "test"};
System.out.println(a[0], a[1], a[2]);
Answer
- A readable and writeable array should be ***INVARIANT***.
That's why we say `String[]` is not subtype of `Object[]`
- A readonly array is **covariant**.
Instead, we can say `readonly String[]` is a subtype of `readonly Object[]`
Extension
- So this is typical static typing problem in Java (as well as C#).
- Guess Why?
Root cause
- Yes, GENERICS. Java and C# does not support generics in old time.
They use parent typing like the generic bounding to make functions accept more generic types.
```Java
boolean equalArrays (Object[] a1, Object[] a2); // equal function should be readonly, which is safe.
void shuffleArray(Object[] a);
```
- It should be defined like this.
```Java
<T extends Comparable> boolean equalArrays (T[] a1, T[] a2);
void shuffleArray(T[] a);
```
- Today, the legacy feature is a burden now.
Use it must take care of if the array is writable to avoid runtime errors.
Or use some immutable/readonly array instead rather a raw object array. (of course, they introduces overhead before Java/C# introdues raw immutable data type primitives)
In C# :
```cs
IEnumerable