Lets get typing JaveScript

Here's a little story about JS

import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.transferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError();
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.transferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError(); // Error 💥
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.transferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError(); // Error 💥
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => console.log(res.transferFee)); // Log: undefined
  }, [])
  
  const handleTransfer = () => {
    console.log(transferAmount, transferFee); // Log: 399, undefined
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError(); // Error 💥
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => console.log(res.transferFee)); // Log: undefined
  }, [])
  
  const handleTransfer = () => {
    console.log(transferAmount, transferFee); // Log: 399, undefined
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError(); // Error 💥
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}

So its time to talk with backend guys

import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => console.log(res)); // Log: { Currency: "DKK", TransferFee: 4.00 } ✅
  }, [])
  
  const handleTransfer = () => {
    console.log(transferAmount, transferFee); // Log: 399, undefined
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError(); // Error 💥
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.TrasferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError();
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.TrasferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError();
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.TrasferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete); // 💥 { amountWithFee: "3994.00" }
    else
      onError();
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.TrasferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete); // ✅ { amountWithFee: "403.00" }
    else
      onError();
  }
  
  const handleChange = (value) => setTransferAmount(parseFloat(value));
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={handleChange} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.TrasferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete); // ✅ { amountWithFee: "403.00" }
    else
      onError();
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} type="number" />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


const TransferInput = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState(null);
  const [transferAmount, setTransferAmount] = useState(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.TrasferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete); // ✅ { amountWithFee: 403.00 }
    else
      onError();
  }
  
  const handleChange = (value) => setTransferAmount(parseFloat(value));
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={handleChange} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}

TypeScript

TypeScript

What

Why

How

TypeScript

  • Typed superset of JavaScript
     
  • Compiles to plain JavaScript
     
  • C# inspired syntax & type system
     
  • Made by Microsoft

Why

Why

  • Static type checking
     
  • Refactoring tools
     
  • Improved DX
     
  • Easier onboardimg
     
  • Better collaboration across areas

How

import { getTransferFee, transferMoneyNow } from 'awesome-apis';


interface ITransferInputProps {
  onComplete: () => void;
  onError: () => void;
}
const TransferInput: React.FunctionComponent<ITransferInputProps> = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState<number>(null);
  const [transferAmount, setTransferAmount] = useState<number>(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.transferFee));
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError();
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} />
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


interface ITransferInputProps {
  onComplete: () => void;
  onError: () => void;
}
const TransferInput: React.FunctionComponent<ITransferInputProps> = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState<number>(null);
  const [transferAmount, setTransferAmount] = useState<number>(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.transferFee)); // Property 'transferFee' does not exist on type IGetTransferFeeResponse. Did you mean 'TransferFee'?
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete); // Type error: string is not assignable to number
    else
      onError();
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} /> // Type error: string is not assignable to number 
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}
import { getTransferFee, transferMoneyNow } from 'awesome-apis';


interface ITransferInputProps {
  onComplete: () => void;
  onError: () => void;
}
const TransferInput: React.FunctionComponent<ITransferInputProps> = ({onComplete, onError}) => {
  
  const [transferFee, setTransferFee] = useState<number>(null);
  const [transferAmount, setTransferAmount] = useState<number>(null);
  
  React.userEffect(() => {
    getTransferFee().then(res => setTransferFee(res.TransferFee)); 
  }, [])
  
  const handleTransfer = () => {
    if(transferAmount && transferFee)
    	transferMoneyNow({ amountWithFee: transferAmount + transferFee }).then(onComplete);
    else
      onError();
  }
  
  return (
    <TransferView>
    	<Input value={transferAmount} onChange={setTransferAmount} type="number" /> 
    	<Button text="Transfer" onPress={handleTransfer} />
    </TransferView>
  );

}

Whaaat?

Whaaat?

How did type="number" fix the type error?

interface IInputProps {
  value: string;
  onChange: (value: string) => void;
}
const Input: React.FunctionComponent<IInputProps> = (props) => {
  return (
    <FabricInput style="denim" value={props.value} onChange={props.onChange}/>
  );
}
interface IInputProps {
  type: "text" | "number";
  value: string | number;
  onChange: (value: string | number) => void;
}
const Input: React.FunctionComponent<IInputProps> = (props) => {
  return (
    <FabricInput style="denim" value={props.value} onChange={props.onChange}/>
  );
}
type AllowedTypes = "text" | "number";
type TypeToValueType<Type extends AllowedTypes> = 
	Type extends "text" 
		? string
		: Type extends "number" 
			? number 
			: string | number;

interface IInputProps<Type extends AllowedTypes = "text"> {
  type?: Type;
  value: TypeToValueType<Type>;
  onChange: (value: TypeToValueType<Type>) => void;
}
const Input = <Type extends AllowedTypes>(props: IInputProps<Type>) => {
  return (
    <FabricInput style="denim" value={props.value} onChange={props.onChange}/>
  );
}
type AllowedTypes = "text" | "number";
type TypeToValueType<Type extends AllowedTypes> = 
	Type extends "text" 
		? string
		: Type extends "number" 
			? number 
			: string | number;

interface IInputProps<Type extends AllowedTypes = "text"> {
  type?: Type;
  value: TypeToValueType<Type>;
  onChange: (value: TypeToValueType<Type>) => void;
}
const Input = <Type extends AllowedTypes>(props: IInputProps<Type>) => {
  return (
    <FabricInput style="denim" value={props.value} onChange={props.onChange}/> // 💥
  );
}
type AllowedTypes = "text" | "number";
type TypeToValueType<Type extends AllowedTypes> = 
	Type extends "text" 
		? string
		: Type extends "number" 
			? number 
			: string | number;

interface IInputProps<Type extends AllowedTypes = "text"> {
  type?: Type;
  value: TypeToValueType<Type>;
  onChange: (value: TypeToValueType<Type>) => void;
}
const Input = <Type extends AllowedTypes>(props: IInputProps<Type>) => {
  return (
    <FabricInput style="denim" value={props.value} onChange={props.onChange}/> // 💥
  );
}
type AllowedTypes = "text" | "number";
type TypeToValueType<Type extends AllowedTypes> = 
	Type extends "text" 
		? string
		: Type extends "number" 
			? number 
			: string | number;

interface IInputProps<Type extends AllowedTypes = "text"> {
  type?: Type;
  value: TypeToValueType<Type>;
  onChange: (value: TypeToValueType<Type>) => void;
}
const Input = <Type extends AllowedTypes>(props: IInputProps<Type>) => {
  
  const getValue = (value: string | number) => {
    return props.type === "number" ? props.value.toString() : props.value;
  }
  
  const handleChange = (value: string) => {
    props.onChange(props.type === "number" ? parseFloat(value) : props.value);
  }
  
  return (
    <FabricInput style="denim" value={getValue(props.value)} onChange={handleChange}/>
  );
}
// Infer types
const pickItem = <List extends any[], Index extends number>(list: List, index: Index): List[Index]  => {
  return list[index];
}

const a = pickItem(["a", 1], 1); // a: string | number;
// Infer types
const pickItem = <List extends any[], Index extends number>(list: List, index: Index): List[Index]  => {
  return list[index];
}

const a = pickItem(["a", 1], 1); // a: string | number;

const pickItem = <List extends ReadonlyArray<any>, Index extends number>(list: List, index: Index): List[Index]  => {
  return list[index];
}

const a = pickItem(["a", 1] as const, 1); // a: number;
// Nullish Coalescing & Optional Chaining
interface IUser {
  email: 'john@doe.com'
  username: 'JohnDoe88',
  info?: {
    firstName: string;
    lastName: string;
    balance?: number 
  }
}

interface IBalanceInfoProps {
  user?: IUser;
}

const BalanceInfo = (props: IBalanceInfoProps) => {
  const balance = user && user.info && user.info.balance;
  
  return (
    <Text>{balance ? balance : "Not available"}</Text>
  );
};
// Nullish Coalescing & Optional Chaining
interface IUser {
  email: 'john@doe.com'
  username: 'JohnDoe88',
  info?: {
    firstName: string;
    lastName: string;
    balance?: number 
  }
}

interface IBalanceInfoProps {
  user?: IUser;
}

const BalanceInfo = (props: IBalanceInfoProps) => {
  const balance = user && user.info && user.info.balance;
  
  return (
    <Text>{balance ? balance : "Not available"}</Text>
  );
};

// 💥 Shows "Not available" instead of 0...
// Nullish Coalescing & Optional Chaining
interface IUser {
  email: 'john@doe.com'
  username: 'JohnDoe88',
  info?: {
    firstName: string;
    lastName: string;
    balance?: number 
  }
}

interface IBalanceInfoProps {
  user?: IUser;
}

const BalanceInfo = (props: IBalanceInfoProps) => {
  const balance = user?.info?.balance;
  
  return (
    <Text>{balance ?? "Not available"}</Text>
  );
};

// ✅ Shows 0 instead of "Not available"
// Discriminated Unions
type Shape = {
    kind: "square" | "rectangle" | "circle";
    size?: number;
    width?: number;
    height?: number;
    radius?: number;
}

interface IAwesomeShapeProps<S extends Shape> {
  shape: S;
}

const AwesomeShape = <S extends Shape>(props: IAwesomeShapeProps<S>) => {
  if (props.shape.kind === "square")
    return <Square size={props.shape.size || 0} />;
    
  if (props.shape.kind === "rectangle")
    return <Rectangle width={props.shape.size || 0} height={props.shape.size || 0} />;
    
  if (props.shape.kind === "circle")
    return <Circle radius={props.shape.radius || 0} />;
    
    
};
// Discriminated Unions
type Shape = {
    kind: "square" | "rectangle" | "circle";
    size?: number;
    width?: number;
    height?: number;
    radius?: number;
}

interface IAwesomeShapeProps<S extends Shape> {
  shape: S;
}

const AwesomeShape = <S extends Shape>(props: IAwesomeShapeProps<S>) => {
  if (props.shape.kind === "square")
    return <Square size={props.shape.size || 0} />;
    
  if (props.shape.kind === "rectangle")
    return <Rectangle width={props.shape.size || 0} height={props.shape.size || 0} />;
    
  if (props.shape.kind === "circle")
    return <Circle radius={props.shape.radius || 0} />;
    
    return <Text>{`Missing props for kind ${props.shape.kind}`}</Text>;
    
};
// Discriminated Unions
interface ISquare {
    kind: "square";
    size: number;
}
interface IRectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface ICircle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

interface IAwesomeShapeProps<S extends Shape> {
  shape: S;
}

const AwesomeShape = <S extends Shape>(props: IAwesomeShapeProps<S>) => {
  if (props.shape.kind === "square")
    return <Square size={props.shape.size} />;
    
  if (props.shape.kind === "rectangle")
    return <Rectangle width={props.shape.size} height={props.shape.size} />;
    
  if (props.shape.kind === "circle")
    return <Circle radius={props.shape.radius} />;
};

Summary

Basic Types

Interfaces

Generics

Generics

Conditional Types

Summary

Summary

Summary

Summary

Nice to knows

Nice to knows

utility-types

utility

immer-reducer

state system

80% devs wants TS

state of JS

Use strict mode

tip

async, readonly...

new features

better error messages

TS 3.7

Nice to knows

utility-types

utility

immer-reducer

state system

80% devs wants TS

state of JS

Use strict mode

tip

async, readonly...

new features

better error messages

TS 3.7

Have a great day 🥳

Lets get typing Javascript

By thupi

Lets get typing Javascript

  • 201