theme | class | highlighter | lineNumbers | info | drawings | transition | title | layout | background | |
---|---|---|---|---|---|---|---|---|---|---|
default |
text-center |
shiki |
false |
## Slidev Starter Template
Presentation slides for developers.
Learn more at [Sli.dev](https://sli.dev)
|
|
slide-left |
Typescript talk |
cover |
/inugami_korone_effective_typescript.png |
Pendle Finance SDK developer.
- Handle smart contract interaction and backend data processing logic.
- Use Typescript as primary language.
Restriction is not always bad.
But too much freedom can lead to very dangerous things.
null
pointer.- Unsafe cast.
- Calling undefined method.
- Un-sanitized user input.
- Zero-access control
- Calling unintentional methods.
- Calling method the wrong way.
- Self-modifying code
- Javascript's
eval
.
- Javascript's
- Restriction prevents programmers from doing bad things.
- Restriction guides programmers to do the correct things.
- Restriction does not need to be painful.
- Rust borrow checker.
null
check.- Linter rules.
- Pureness check.
- Type system.
Mom, can we have type safety?
No, we have type safety at home runtime.
Type safety at runtime:
function isEven(a) {
assert(typeof a == 'number');
return a % 2 == 0;
}
function isOdd(a) {
assert(typeof a == "number");
return isEven(a);
}
hideInToc: tru transition: slide-up
Tool with more restrictions to enforce correctness, and can even detect defects and vulnerabilities.
- Cpp: cppcheck, Clang Static Analyzer, clang-tidy.
- Solidity: solhint, slither, smtchecker.
- Rust: rust-analyzer, clippy.
- Javascript: eslint, jshint.
- Typescript: eslint, typescript-eslint.
Some good and interesting rules for both JS and TS.
/*eslint for-direction: "error"*/
for (let i = 0; i < 10; i--) {}
for (let i = 10; i >= 0; i++) {}
for (let i = 0; i > 10; i++) {}
/*eslint no-unmodified-loop-condition: "error"*/
let node = something;
while (node) { doSomething(node); }
node = other;
for (let j = 0; j < items.length; ++i) {
doSomething(items[j]);
}
/*eslint no-unreachable: "error"*/
function foo() {
return true;
console.log("done");
}
function bar() {
throw new Error("Oops!");
console.log("done");
}
while(value) {
break;
console.log("done");
}
function baz() {
if (Math.random() < 0.5) { return; }
else { throw new Error(); }
console.log("done");
}
JS eslint vs TS
const myArray = ['a', 'b', 'c'];
// create { a: 1, b: 2, c: 3 };
const indexMap = myArray.reduce((memo, item, index) => {
memo[item] = index;
}, {});
const math = Math();
const newMath = new Math();
const json = JSON();
const newJSON = new JSON();
class Foo {
x = 1;
constructor() {}
}
class Bar extends Foo {
constructor() {
this.y = this.x + 1;
super();
}
}
Linting with @typescript-eslint/recommended-requiring-type-checking
- Working with promises
const promise = Promise.resolve('value');
if (promise) {}
const promise = Promise.resolve('value');
if (await promise) {}
async function invalidInTryCatch1() {
try {
return Promise.resolve('try');
} catch (e) {}
}
async function validInTryCatch1() {
try {
return await Promise.resolve('try');
} catch (e) {}
}
[1, 2, 3].forEach(async value => {
await doSomething(value);
});
Promise.all(
[1, 2, 3].map(async value => {
await doSomething(value);
}),
).catch(handleError);
// or use for-of
Linting with @typescript-eslint/recommended-requiring-type-checking
- no
any
// fail with no-explicit-any
const age: any = 'seventeen';
// fail with no-unsafe-assignment
const x = 1 as any;
// no-unsafe-argument
function a(x: number, y: number) {}
const args: any[] = [];
a(...args);
const age: number = 17;
const x = 1;
function a(x: number, y: number) {}
const args: [number, number] = [1, 2];
a(...args);
class Foo {};
console.log(`value=${new Foo()}`);
// value=[object Object]
class Foo { toString() { return 'Foo'; }}
console.log(`value=${value}`);
// value=Foo
Example: Dependency injection in Nest.js
import { Controller, Get, Post, Body } from "@nestjs/common";
import { CreateCatDto } from "./dto/create-cat.dto";
import { CatsService } from "./cats.service";
import { Cat } from "./interfaces/cat.interface";
@Controller("cats")
export class CatsController {
constructor(private catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
In tsconfig.json
{
"compilerOptions": {
"emitDecoratorMetadata": true
}
}
- google-closure-compiler
- Compile and optimize Javascript code.
- Use
jsdoc
for typing. - Example:
console.log(3,"xy");
- Tsickle
- Compile TS -> JS but with JSDoc. Then compile it with closure compiler.
- Not ready for public use. Used internally in google.
https://www.typescriptlang.org/docs/handbook/type-compatibility.html
interface Pet {
name: string;
}
class Dog { // looks ma, no implements
name: string;
}
let pet: Pet;
// OK, because of structural typing
pet = new Dog();
// dog's inferred type is { name: string; owner: string; }
let dog = { name: "Lassie", owner: "Rudd Weatherwax" };
pet = dog;
The following give errors , as that object literals may only specify known properties:
pet = { name: "Lassie", owner: "Rudd Weatherwax" };
- tsconfig.json
{
"compilerOptions": {
"strictNullCheck": true,
// Or just use "strict": true 👍
}
}
null
andundefined
are now separate types!number
is not the same asnumber | undefined
ornumber | null
.
Always check your null first!
const users = [
{ name: 'foo', age: 20 },
{ name: 'bar', age: 30 },
];
const user = users.find((u) => u.name === 'baz');
// ^? const user: { name: string, age: number } | undefined
Error
console.log(user.age);
// 'user' is possibly 'undefined'.
Correct
if (user != null) {
console.log(user.age);
}
B..but I don't wanna
let user = users.find((u) => u.name === 'baz');
user ??= { name: 'baz', age: 50 };
console.log(user.age); // 50
const user = users.find((u) => u.name === 'baz');
const defaultUser = user ?? { name: 'baz', age: 50 };
console.log(defaultUser); // 50
const user = users.find((u) => u.name === 'baz');
console.log(user?.age); // undefined
console.log(user?.age ?? 50); // 50
console.log(
user?.age?.toString(16) ?? 'cant convert to hex'
);
More Optional chaining!
const arr: number[] | null = null;
const obj: Record<string, string> | null = null;
console.log(arr?.[0]); // undefined
console.log(obj?.['a' + 'b']); // undefined
const func: ((a: number, b: number) => number) | null = null;
console.log(func?.(1, 2)); // undefined
Don't use ||
!
const a: number | null = null
const b: number | null = 0;
console.log(a || 10, a ?? 10); // 10 10
console.log(b || 10, b ?? 10); // 10 0
Combine structurally-unrelated types into one
const a: number | string = 1; // ✅ ok
const b: number | string = '1'; // ✅ ok
const c: number | string = false; // ❌ nope
interface FullName { fullName: string };
interface PartialName { firstName: string; lastName: string };
type Name = FullName | PartialName;
const foo: Name = { fullName: 'John Doe' }
const bar: Name = { firstName: 'Jane', lastName: 'Doe' };
Literals are types too!
type BinaryDigits = 0 | 1;
type HumanReadableBoolean = boolean | 'on' | 'off';
type DoYouLoveMeQuestionMark =
'Yes!' | 'Absolutely!' | 'Definitely!';
type LogLevel =
| 'off' | 'debug' | 'aggressive'
| 0 | 1 | 2;
type Shape =
| { type: 'circle';
x: number; y: number; r: number; }
| { type: 'rectangle';
x: number; y: number; w: number; h: number; };
type NullableString = string | null | undefined;
let zero: BinaryDigit = 0; // ✅
let two: BinaryDigit = 2; // ❌
let onValue: HumanReadableBoolean = 'on'; // ✅
let wrongOnValue: HumanReadableBoolean = 1; // ❌
let nope: DoYouLoveMeQuestionMark = 'NO'; // ❌
let logLv: LogLevel = 'debug'; // ✅
let logLvNum: LogLevel = 2; // ✅
let invalidLogLv: LogLevel = 'info'; // ❌
let invalidLogLvNum: LogLevel = 3; // ❌
let circle: Shape = { type: 'circle', x: 0, y: 0 }; // ✅
let rec: Shape = { // ✅
type: 'rectangle', x: 0, y: 0, w: 10, h: 20
};
let line: Shape = { // ❌
type: 'line', x1: 0, y1: 0, x2: 10, y2: 10
};
Using typeof
type LogLevelNum = 0 | 1 | 2;
type LogLevelText = 'off' | 'debug' | 'aggressive';
type LogLevel = LogLevelNum | LogLevelText;
function resolveLogLevel(logLevel: LogLevel): LogLevelNum {
if (typeof logLevel === 'number') {
return logLevel;
}
if (logLevel == 'off') return 0;
if (logLevel == 'debug') return 1;
if (logLevel == 'aggressive') return 2;
assert(false);
}
Control flow analysis
type LogLevelNum = 0 | 1 | 2;
type LogLevelText = 'off' | 'debug' | 'aggressive';
type LogLevel = LogLevelNum | LogLevelText;
const LOG_LEVELS = ['off', 'debug', 'aggressive'] as const;
// ^? const LOG_LEVELS: readonly ['off', 'debug', 'aggressive']
function logLevelToString(logLevel: LogLevel): LogLevelText {
if (typeof logLevel === 'string') return logLevel;
return LOG_LEVELS[logLevel];
}
Control flow analysis
With branching
function withIfElse(
x: number | string
) {
// x is number | string
if (typeof x === 'number') {
// x is number
} else {
// x is string
}
// x is number | string
}
With return
function withReturn(
x: number | string
) {
// x is number | string
if (typeof x === 'number') {
// x is number
return ;
}
// x is string
}
With throw
function withReturn(
x: number | string
) {
// x is number | string
if (typeof x === 'number') {
// x is number
throw new Error('x should not be number');
}
// x is string
}
Equality narrowing
type LogLevelNum = 0 | 1 | 2;
type LogLevelText = 'off' | 'debug' | 'aggressive';
type LogLevel = LogLevelNum | LogLevelText;
function resolveLogLevel(logLevel: LogLevel): LogLevelNum {
if (logLevel == 'off') return 0;
if (logLevel == 'debug') return 1;
if (logLevel == 'aggressive') return 2;
return logLevel;
}
type DoYouLoveMeQuestionMark = 'Yes!' | 'Absolutely!' | 'Definitely!';
function shoutAnswer(answer?: DoYouLoveMeQuestionMark): string {
if (answer == null) return 'Silent :(';
return answer.toUpperCase();
}
Using instanceof
class Duck {
quack() {
console.log('Quack!');
}
}
class Horse {
neigh() {
console.log('Neigh!');
}
}
type Animal = Duck | Horse;
function makeNoise(animal: Animal) {
if (animal instanceof Duck) {
animal.quack();
}
animal.neigh();
}
makeNoise(new Duck()); // Quack!
makeNoise(new Horse()); // Neigh!
Discriminated union
type Shape =
| { type: 'circle'; x: number; y: number; r: number; }
| { type: 'rectangle'; x: number; y: number; w: number; h: number; };
function calcArea(shape: Shape) {
if (shape.type === 'circle') {
return Math.PI * shape.r ** 2;
}
return shape.w * shape.h;
}
Type predicate functions
type Circle = {type: 'circle'; r: number;};
type Rectangle = {type: 'rectangle'; w: number; h: number;};
type Shape = Circle | Rectangle;
function isCircle(shape: Shape): shape is Circle {
return shape.type === 'circle';
}
function isRectangle(shape: Shape): shape is Rectangle {
return shape.type === 'rectangle';
}
function calcArea(shape: Shape) {
if (isCircle(shape)) return Math.PI * shape.r ** 2;
return shape.w * shape.h;
}
const a: Shape[] = [];
const b = a.filter((shape) => shape.type === 'circle');
// ^? const b: Shape[];
const c = a.filter(isCircle);
// ^? const c: Circle[];
const d = a.filter((shape): shape is Circle =>
shape.type === 'circle'
);
Type assertion functions
type LogLevelNum = 0 | 1 | 2;
type LogLevelText = 'off' | 'debug' | 'aggressive';
type LogLevel = LogLevelNum | LogLevelText;
function assertLogLevelIsSilent(logLevel: LogLevel): asserts logLevel is 0 | 'off' {
if (logLevel !== 0 && logLevel !== 'off') {
throw new Error(`log level should be silent, but found ${logLevel}`);
}
}
function silentProcess(logLevel: LogLevel) {
assertLogLevelIsSilent(logLevel);
// logLevel is now 0 or 'off'
}
There are still more!
- Truthiness narrowing
- The
in
operator narrowing - Assignment
never
type- Exhaustiveness checking
More info can be found in https://www.typescriptlang.org/docs/handbook/2/narrowing.html
https://definitelytyped.github.io/
The repository for high quality TypeScript type definitions
Examples for JQuery:
npm install --save-dev @types/jquery
https://github.com/ts-essentials/ts-essentials
All basic TypeScript types in one place 🤙
npm install --save-dev ts-essentials
Remember to turn on strict mode!
Examples
type A = AsyncOrSync<number>;
// ^? number | PromiseLike<number>
type B = MarkOptional<{ a: number; b: string; c: boolean }, 'a' | 'c'>;
// ^? { a?: number; b: string; c?: boolean }
type Company = {
name: string;
employees: { name: string }[];
}
type C = DeepPartial<Company>;
// ^? { name?: string | undefined; employees?: ({ name?: string | undefined } | undefined)[] | undefined }
https://github.com/millsp/ts-toolbelt
TypeScript's largest utility library, featuring +200 utilities!
npm install ts-toolbelt --save
Remember to turn on strict mode!
Examples:
import {Object} from "ts-toolbelt"
// Check the docs below for more
// Merge two `object` together
type merge = Object.Merge<{name: string}, {age?: number}>
// {name: string, age?: number}
// Make a field of an `object` optional
type optional = Object.Optional<{id: number, name: string}, "name">
// {id: number, name?: string}
TypeScript-first schema validation with static type inference
npm install zod
Usage:
Creating a simple string schema
import { z } from "zod";
// creating a schema for strings
const mySchema = z.string();
// parsing
mySchema.parse("tuna"); // => "tuna"
mySchema.parse(12); // => throws ZodError
// "safe" parsing (doesn't throw error if validation fails)
mySchema.safeParse("tuna"); // => { success: true; data: "tuna" }
mySchema.safeParse(12); // => { success: false; error: ZodError }
Creating an object schema
import { z } from "zod";
const User = z.object({
username: z.string(),
});
User.parse({ username: "Ludwig" });
// extract the inferred type
type User = z.infer<typeof User>;
// { username: string }
ArkType is a runtime validation library that can infer TypeScript definitions 1:1 and reuse them as highly-optimized validators for your data.
npm install arktype
Example:
import { type } from "arktype"
// Definitions are statically parsed and inferred as TS.
export const user = type({
name: "string",
device: {
platform: "'android'|'ios'",
"version?": "number"
}
})
// Validators return typed data or clear,
// customizable errors.
export const { data, problems } = user({
name: "Alan Turing",
device: {
// problems.summary: "device/platform
// must be 'android' or 'ios' (was 'enigma')"
platform: "enigma"
}
})
Typescript true power!
https://github.com/grantila/meta-types
TypeScript meta functions for (especially variadic) meta programming
npm install meta-types
Examples
import type { Add, Sub, Mul, If, GreaterThan } from 'meta-types'
type T1 = Add< 13, 11 >; // T1 is 24
type T2 = Sub< 13, 11 >; // T2 is 2
type T3 = Mul< 13, 11 >; // T3 is 143
type T4 = If< true, "yes", "no" >; // T4 is "yes"
type T5 = If< false, "yes", "no" >; // T5 is "no"
type T6 = GreaterThan< 42, 40 >; // T6 is true; 42 > 40
type T7 = GreaterThan< 40, 42 >; // T7 is false; 40 < 42
https://github.com/gvergnaud/hotscript
A library of composable functions for the type level!
npm install -D hotscript
Examples:
import { Pipe, Tuples, Strings, Numbers } from "hotscript";
type res1 = Pipe<
// ^? 62
[1, 2, 3, 4],
[
Tuples.Map<Numbers.Add<3>>, // [4, 5, 6, 7]
Tuples.Join<".">, // "4.5.6.7"
Strings.Split<".">, // ["4", "5", "6", "7"]
Tuples.Map<Strings.Prepend<"1">>, // ["14", "15", "16", "17"]
Tuples.Map<Strings.ToNumber>, // [14, 15, 16, 17]
Tuples.Sum // 62
]
>;
const a = resolveLogLevel('off');
const b = resolveLogLevel(1);
const c = resolveLogLevel('aggressive');
Current typing:
const a: 0 | 1 | 2;
const b: 0 | 1 | 2;
const c: 0 | 1 | 2;
Better typing:
const a: 0;
const b: 1;
const c: 2;
Function overloading
function resolveLogLevel(logLevel: 'off'): 0;
function resolveLogLevel(logLevel: 'debug'): 1;
function resolveLogLevel(logLevel: 'aggressive'): 2;
function resolveLogLevel(logLevel: 0): 0;
function resolveLogLevel(logLevel: 1): 1;
function resolveLogLevel(logLevel: 2): 2;
Syntax
type X = A extends B ? TrueBranchType : FalseBranchType;
Examples
type A = 'off';
type B = 0;
type C = 'off';
type UnderlyingTypeOfA = A extends string ? string : number; // string
type UnderlyingTypeOfB = B extends string ? string : number; // number
type IsAOff = A extends 'off' ? true : false; // true
type IsAEqB = A extends B ? (B extends A ? true : false) : false; // false
type IsAEqC = A extends C ? (C extends A ? true : false) : false; // true
<iframe width="900" height="450" src="https://www.typescriptlang.org/play?#code/C4TwDgpgBAglC8UAMBuAUAeg1UlYKgHIB7AM1MPSx3GjkUIBMIAjAVwHNLNtc6DCAQw4cAThADOEgJYA3CNzR8oAJUnEANvMYAZYhx0R5GgGLFR9NFGv4IAD2AQAdowlEyFKAH5kVqAC5bB2dXImZ2Lm8oAEY-QLh7Rxc3IRFxKTkFKIAmONgUIA" />
Syntax
type GenericType<TypeParam1 [extends Contraints1], TypeParam2 [extends Constraints2], ...> = TypeDefinition;
Examples:
type Identity<T> = T;
type Nullable<T> = T | null | undefined;
type Matrix<T> = T[][];
type Vector<T extends (number | bigint)> = { x: T; y: T };
type ConcatTyple<A extends unknown[], B extends unknown[]> = [...A, ...B];
type UnderlyingType<A> = A extends string ? string : number;
type IsOff<A extends string> = A extends 'off' ? true : false;
type Eq<A, B> = A extends B ? (B extends A ? true : false) : false;
<iframe width="900" height="450" src="https://www.typescriptlang.org/play?#code/C4TwDgpgBAMg9gcxhAbhANlAvFA5HAMwNygB88ATCAIwFcETzcBDBBAJwgGcuBLNRlAAMZKAEZRAJgDcAKFmhIUAErc46NBXhJUGADwBBKBAAewCADsKXWImRp0APmyyobqEdPmrN-ERIA-MKuUABcHsZmltaUNPSB4iHhnlE+eKwc3HwCUEGSSR5yCuDQRjiqXOqa2vb6fsSOcgD0Te4AegHFSgBC2CpqGhBadrroekKNsi3tnYrQAMJ9FVVDNaN6LGycPPwQuJPTbh1d0AAiSwPVIw7jopuZOwIHrUedQA" />
<iframe width="900" height="450" src="https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAJwKYGc4BsBuqAycA5vqnlgDz46KoAeUqYAJuooSWalgHwAUWYqXIAudjgCUYgEoZseZh2HcqOHgG4AUKEiwEKObgJCuWASdHsL3KVc7kAciAC2iAN6bEiGMETn73IgAvCGIAORwwMBhEgZQIMhIAAxaXj5+ggFYwaFhzKgARiBEMXEJSACMqd6+-srZIUHhAIZERGjo6DB4pWjxiYgATNV95YiZ9VoAvpoQCOhQiM3BBphGSqZ8eYXFMVoA9PteXgB6APyz84sFKx3yxll8SRIHR8fnl2ALiBC3hngbchbVrtDBdHovTSHY6ID6aTRQACeAAdUHZ6isIlEwogAD7hfJFEp4lptDrg1A4-FJEkVEnDBEotGA7hOVxNGn4un4hmM1GIWRrBQsygAQVoDCYrHRph4wU8x3F9EYLDYWOiiDOiCSCrESslqoJO2JWoqusQ+pV0rCIPJ3UpmqG5tFWk0Hi8Omg8CQd3W1iwAHUYFAABYAUWQyDgyFUEqtbBF-AmpjE1Fsgvuin9qjl7uO6TqphyTXVvVQ-WS1TStWT5GLRqJZYriCqCurGX99ZtZLB9qbYwZMNGA1r3GqMymQA" />
Syntax
type Value = Obj[Index];
Where Obj
should be an object type, and Index
should be key of Obj
(should extends keyof Obj
).
Examples:
type Obj = {
a: number;
b: string;
c: boolean;
};
type Tuple = [number, string, boolean];
type A = Obj['a']; // number
type B = Obj['b']; // string
type C = Obj['c']; // boolean
type T0 = Typle[0]; // number
type T1 = Tuple[1]; // string
type T2 = Tuple[2]; // boolean
// number | string | boolean
type ValueOfObj = Obj[keyof Obj];
type ValueOfTuple = Tuple[keyof Tuple];
// number | boolean
type AorC = Obj['a' | 'c'];
type T0or2 = Tuple[0 | 2];
type ResolvedLogLevelMap = {
0: 0;
1: 1;
2: 2;
'off': 0;
'debug': 1;
'aggressive': 2;
};
type LogLevel = keyof ResolvedLogLevelMap;
type ResolvedLogLevel<Lv extends LogLevel> = ResolvedLogLevelMap[Lv];
{ "name": "decimals",
"inputs": [],
"outputs": [ { "type": "uint8" } ],
}
type Fn = () => [number];
{ "name": "transferFrom",
"inputs": [
{ "name": "from", "type": "address" },
{ "name": "to", "type": "address" },
{ "name": "amount", "type": "uint256" }
],
"outputs": [ { "type": "bool" } ],
}
type Fn = (
from: string, to: string, amount: bigint
) => [bool];
{ "name": "observations",
"inputs": [ { "type": "uint256" } ],
"outputs": [
{ "name": "blockTimestamp", "type": "uint32" },
{ "name": "lnImpliedRateCumulative",
"type": "uint216" },
{ "name": "initialized", "type": "bool" }
],
}
type Fn = (_param: bigint): [bigint, bigint, boolean];
type MapPrimitiveType<T> =
T extends 'bool' ? boolean
: T extends 'address' ? string
: T extends 'uint8' ? number
: T extends `uint${number}` ? bigint
: never;
Syntax
type StringType = `x${T1}y${T2}z`;
Examples
type AddMrTitle<Name extends string> = `Mr. ${Name}`;
type SimpleEmail = `${string}@${string}.${string}`;
type HexString = `0x${string}`;
type ContractIdentity = `${HexString /* chain id */}-${HexString /* contract address */}`;
type TestUint256 = 'uint256' extends `uint${number}` ? true : false; // true
type MapPrimitiveArrayType<Arr> =
Arr extends readonly [] ? []
: Arr extends readonly [{type: infer HeadType} , ...infer Rest]
? [MapPrimitiveType<HeadType>, ...MapPrimitiveArrayType<Rest>]
: never;
Syntax
type T = A extends SomeType<infer T> ? /* do something with T */ : FalseBranchType;
Examples:
type ArrayElementType<Arr> = Arr extends (infer T)[] ? T : never;
type FlattenArrayType<Arr> = Arr extends (infer T)[] ? FlattenArrayType<T> : Arr;
type GetFirstElementType<Arr> = Arr extends [infer First, ...infer _Rest] ? First : never;
type GetType<Entry> = Entry extends { type: infer T } ? T : never;
type AbiToFunc<Abi> =
Abi extends { inputs: infer Input; outputs: infer Output }
? (...params: MapPrimitiveArrayType<Input>) => MapPrimitiveArrayType<Output>
: never;
declare function generateFunction<Abi>(abi: Abi): AbiToFunc<Abi>;
<iframe width="900" height="450" src="https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAWwIYzACgJSIN4BQiiECAzlIgCYCmEMaANgGLgSIC8iA5jWDQCdUUGq0iwEmACIBRAMIBJALIBBADIBlAPoqAQguwBuIogD0p4sQB6AfhOkwFRFCGPgg5gLjIx7Lr34hEV8JLAAVACUVADkNZhkIrWYIgHklHX0jE3NLRFt7cko4ACMyQQA3YXhHT3BOHj5BYVE2UMwU3Q0EgDUVMIUU2IyDY2Icy3yAXwICBydZRVVNYfrCYgAiMFRkGnWALkR12nomMnWAGhN1jAAHECgzg4BtAF1Ljbh7u4f9xCe8Q5QACeN12B3WIAwUAAHOtEJM3gRJohUGQSIVjLNCohIjE4gkkql0noFKsrlsdr91i5UG4PF5kBcrrd7o8-iZiADNtswYdgAyLoCQbz1qgqFQBDQyGd4e9LFyKSKoHBBdThVSxRKpTLJnLOYdFRrkJ8wFBVcDQVTIaaAEwAVgAbHDpsRER8vqzfv8hZbwcU4HBGM7EcjUejHFBMXNKB0uhFev1BtoSWSNobwSUygJKqEznLrmBvmzvWrfYdrVB7U74W7Dp8oEWvRz8AaeVTiow4BAANZhBhSqDbG7m9XgisAZhtzr1+Gbabb4MYYAUyBujBgNCoEWachAyBAjCq5V2M42FpFFZtAEYnc3dc2FQvDhgYLBUOuAF6bkdl9b+wPOiYIYomi0aYgQ56IEoqA3AACgIDCvjAx5hMKAA8YQAHycCYYSIDQAAeIhgFQaIAOT-owZGIDYiCUTQtImAceGEcRpGIGRmqStK1G0RQCFgNwTE4vhRF8OxZEVtCvGIGAe7FIIwksWJJFogABhWAAkeBycgCkCJMak0XRMDcFCwn8MeAiYpB0FwQhyBIceKgCEIQKoaCaEuQI2EcCY3miWxaKSmKCCMECfwvCYtGvMJAWseJwUMVQYURd654HBg7gCIgAASyUeTQsqIAAdGVWWCIgEQDlFxAxXZ8GIbAKHoflYqFZh5ylWVDUOU5NDeag7nodVFCYbViAHJZgijIgEHCogKjFDAYRwL4XnLb5-nLYFiUtiyDyZWA2WIAohb3IYiD1kWR0nSkHqUC6xmYGVJU3KgQjIGQBy9U1yEDa5Q2FWhZ3fJhuAcNhv2Oc1ANucD90NvcmEWTQVmYsch6SogoDiNUDSBM0ITVBtMCYZgqDLQcS0wNg1PLat6005hmJAA" />
`/v1/{chainId:number}/assets`
type Params = {
chainId: number;
};
`/v1/{chainId:number}/assets/{address:string}`
type Params = {
chainId: number;
addrses: string;
};
`/v1/ve-pendle/statistics`
type Params = {};
type MapPrimitiveType<T> =
T extends 'string' ? string
: T extends 'number' ? number
: T extends 'boolean' ? boolean
: never;
type MapPartToObject<Part extends string> =
Part extends `{${infer Prop}:${infer Type}}`
? { [key in Prop]: MapPrimitiveType<Type> } // The same as `Record<Prop, MapPrimitiveType<Type>>`
: {};
Examples:
type ChainId = MapPartToObject<'{chainId:number}'>; // { chainId: number }
type Address = MapPartToObject<'{address:string}'>; // { address: string}
type NoParamPart = MapPartToObject<'tokens'>; // {}
type ParseURLParams<URL extends string> =
URL extends `${infer Part}/${infer Rest}`
? MapPartToObject<Part> & ParseURLParams<Rest>
: MapPartToObject<URL>;
<iframe width="900" height="450" src="https://www.typescriptlang.org/play?#code/C4TwDgpgBACghgJzgWwM4EYoF5aNRAVQCUAZeJNAHgHIB6AN3VoG8BjACzgEsA7ASQAmALh4BXZACMICAL604qfMFTUAfAG4AULVpQ9APQD8m0JFwVUAJmzn8xMohSoaDJm069BI8VNnzFEMoscAICCBCKQqjACLwA5jJqWjp6UEYm4NAAQoHA0uRO1jjkdqQFaDAA9tEwCJWsEc50jCwc3PzCYpLScgpKqMGh4ZHRsTwJSdq6Bsam0OWoAMw2JYRljlTNTPQQALSQPAIANhC00XDAXNFcrCoaU6npms9zUACycGC1XMhclzsAFUylABqmwmlSUABUAgAA88odUFBqKN4tQoIYoKjxhCoEIoTD4RBEciur50ZiydJcfjoXCEQIkdQJJVKic4DwKVAWWyIByaVAeBAdggtBkzB8vohgADKgB5CQAKwgrGAlHIwEJDKR2LiYKwuL0Gq1xMZUAABswACTMXgAM2ksDqYBkQht9sdQMgMhk5txmOYUAA2gBrCAgKC8J2VMAAXXxku+v3+EC9EBBmTBMgFzBkYteq3sC0o9hNJN1+sNUFL9NNSPN7p4DoQ5mAckbzagRAibb9ekxielsoVytV6ulYIAZLY1g4LJRu9FVALBwgZfKlSq1fZ7uL5nhZwsqjU6g1FCXSGWzRWbABhI6VIXjhClOdOC8kVS7173x-pgAaV5IpUm6qvqUCBkGADSkY8FAYYgJUdpQP+8YodBsbqFAMhAA" />
Typescript type system is Turing complete!
Normal programming language | Typescript Type system |
---|---|
Branching | Conditional typing |
Assignment | Conditional typing with infer |
Function | Generic type |
Loop | Recursive generic type |
Brainfuck intepreter in Typescript type system:
import type { Brainfuck } from "@susisu/typefuck";
type Program = ">,[>,]<[.<]";
type Input = "Hello, world!";
// = "!dlrow ,olleH"
type Output = Brainfuck<Program, Input>;
- https://viem.sh/. Ethereum interaction library.
- No Typechain. Only constant ABI.
- https://viem.sh/docs/typescript.html
- https://www.prisma.io/. Next-generation Node.js and TypeScript ORM
- Get only what you have queried!
- Pendle SDK
Check out https://www.typescriptlang.org/docs/handbook/2/types-from-types.html