generated from antfu-collective/vitesse-lite
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor & feat: add ts-behavoir and change folder structure
- Loading branch information
Andrew
committed
Sep 3, 2023
1 parent
544e65d
commit 47cdaa8
Showing
47 changed files
with
266 additions
and
0 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* eslint-disable prefer-const */ | ||
declare let b: string | ||
declare let c: string | number | ||
|
||
c = b // ✅ | ||
|
||
/** | ||
* string is a sub-type of string | number, all elements of string appear in string | number, | ||
* so we can assign b to c. c still behaves as we originally intended it. This is co-variance. | ||
*/ | ||
|
||
/** | ||
* This on the other hand, won’t work: | ||
*/ | ||
|
||
type Fun<X> = (...args: X[]) => void | ||
|
||
declare let f: Fun<string> | ||
declare let g: Fun<string | number> | ||
|
||
g = f // 💥 this cannot be assigned | ||
|
||
/** | ||
* And if you think about it, this is also clear. | ||
* When assigning f to g, we suddenly can’t call g with numbers anymore! We miss part of the contract of g. | ||
* This is contra-variance, and it effectively works like an intersection. | ||
*/ | ||
|
||
/** | ||
* This is what happens when we put contra-variant positions in a conditional type: TypeScript creates an intersection out of it. | ||
* Meaning that since we infer from a function argument, TypeScript knows that we have to fulfill the complete contract. | ||
* Creating an intersection of all constituents in the union. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* Distributive Conditional Types | ||
*/ | ||
|
||
/** | ||
* When conditional types act on a generic type, they become distributive when given a union type. For example, take the following: | ||
*/ | ||
|
||
type ToArray<Type> = Type extends any ? Type[] : never | ||
type StrArrOrNumArr = ToArray<string | number> // string[] | number[] | ||
/** | ||
* What happens here is that StrArrOrNumArr distributes on: | ||
* 1. string | number | ||
* and maps over each member type of the union, to what is effectively: | ||
* 2. ToArray<string> | ToArray<number>; | ||
* 3. string[] | number[] | ||
*/ | ||
|
||
/** | ||
* To avoid this default behavior surround each side of the extends keyword with square brackets | ||
*/ | ||
|
||
type _ToArray<Type> = [Type] extends any ? Type[] : never | ||
type _StrArrOrNumArr = _ToArray<string | number> // (string | number)[] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
export {} | ||
|
||
type _UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends ( | ||
x: infer R | ||
) => any | ||
? R | ||
: never | ||
|
||
interface Format320 { | ||
urls: { format320p: string } | ||
} | ||
interface Format480 { | ||
urls: { format480p: string } | ||
} | ||
interface Format720 { | ||
urls: { format720p: string } | ||
} | ||
interface Format1080 { | ||
urls: { format1080p: string } | ||
} | ||
|
||
interface BasicVideoData { | ||
id: number | ||
} | ||
|
||
type Video = BasicVideoData & (Format320 | Format480 | Format720 | Format1080) | ||
|
||
const video1: Video = { | ||
id: 20, | ||
urls: { | ||
format320p: 'https://...', | ||
}, | ||
} | ||
|
||
// FormatKeys = never, but why? | ||
/** | ||
* Since the Video type is a combination of all these types, | ||
* the urls key from each of the four types is being overridden by the next one, | ||
* so the final object will have only one key urls, but its value can be any of the four possible values | ||
* { format320p: string }, { format480p: string }, { format720p: string }, or { format1080p: string }, | ||
* but never all of them, so the Video["urls"] will have the type of never | ||
*/ | ||
|
||
type FormatKeysFromUnion = keyof Video['urls'] // never | ||
type FormatKeysFromIntersection = keyof _UnionToIntersection<Video['urls']> // format320p | format480p | format 720p | format 1080p | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/** | ||
* MUCH MORE DETAILED DESCRIPTION STEP BY STEP | ||
*/ | ||
|
||
|
||
type UnionToIntersection<T> = | ||
(T extends any ? (x: T) => any : never) extends | ||
(x: infer R) => any ? R : never | ||
|
||
type Intersected1 = UnionToIntersection<Video["urls"]> | ||
|
||
// equals to | ||
|
||
type Intersected2 = UnionToIntersection< | ||
{ format320p: string } | | ||
{ format480p: string } | | ||
{ format720p: string } | | ||
{ format1080p: string } | ||
> | ||
|
||
// we have a naked type, this means we can do | ||
// a union of conditionals: | ||
|
||
type Intersected3 = | ||
UnionToIntersection<{ format320p: string }> | | ||
UnionToIntersection<{ format480p: string }> | | ||
UnionToIntersection<{ format720p: string }> | | ||
UnionToIntersection<{ format1080p: string }> | ||
|
||
// expand it... | ||
|
||
type Intersected4 = | ||
({ format320p: string } extends any ? | ||
(x: { format320p: string }) => any : never) extends | ||
(x: infer R) => any ? R : never | | ||
({ format480p: string } extends any ? | ||
(x: { format480p: string }) => any : never) extends | ||
(x: infer R) => any ? R : never | | ||
({ format720p: string } extends any ? | ||
(x: { format720p: string }) => any : never) extends | ||
(x: infer R) => any ? R : never | | ||
({ format1080p: string } extends any ? | ||
(x: { format1080p: string }) => any : never) extends | ||
(x: infer R) => any ? R : never | ||
|
||
// conditional one! | ||
|
||
type Intersected5 = | ||
(x: { format320p: string }) => any extends | ||
(x: infer R) => any ? R : never | | ||
(x: { format480p: string }) => any extends | ||
(x: infer R) => any ? R : never | | ||
(x: { format720p: string }) => any extends | ||
(x: infer R) => any ? R : never | | ||
(x: { format1080p: string }) => any extends | ||
(x: infer R) => any ? R : never | ||
|
||
// conditional two!, inferring R! | ||
type Intersected6 = | ||
{ format320p: string } | | ||
{ format480p: string } | | ||
{ format720p: string } | | ||
{ format1080p: string } | ||
|
||
// But wait! `R` is inferred from a contra-variant position | ||
// I have to make an intersection, otherwise I lose type compatibility | ||
|
||
type Intersected7 = | ||
{ format320p: string } & | ||
{ format480p: string } & | ||
{ format720p: string } & | ||
{ format1080p: string } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
// type Naked<T> = | ||
// T extends ... // naked! | ||
|
||
/** | ||
* Naked types in conditional types have a certain feature. | ||
* If T is a union, they run the conditional type for each constituent of the union. | ||
* So with a naked type, a conditional of union types becomes a union of conditional types. (A | B)[] => A[] | B[] | ||
*/ | ||
|
||
// type NotNaked<T> = | ||
// { o: T } extends ... // not naked! | ||
|
||
type WrapNaked<T> = T extends any ? { o: T } : never | ||
|
||
type Foo1 = WrapNaked<string | number | boolean> // { o: string; } | { o: number; } | { o: false; } | { o: true; } | ||
|
||
// A naked type, so this equals to | ||
|
||
type Foo2 = WrapNaked<string> | WrapNaked<number> | WrapNaked<boolean> | ||
|
||
// equals to | ||
|
||
type Foo3 = string extends any | ||
? { o: string } | ||
: never | number extends any | ||
? { o: number } | ||
: never | boolean extends any | ||
? { o: boolean } | ||
: never | ||
|
||
type Foo4 = { o: string } | { o: number } | { o: boolean } | ||
|
||
// Not-naked version | ||
|
||
type WrapNotNaked<T> = { o: T } extends any ? { o: T } : never | ||
type WrapNakedModifying<T> = [T] extends [any] ? { o: T } : never | ||
|
||
type Foo5 = WrapNotNaked<string | number | boolean> // { o: string | number | boolean; } | ||
type Foo51 = WrapNakedModifying<string | number | boolean> // { o: string | number | boolean; } | ||
|
||
// A non Naked type, so this equals to | ||
|
||
type Foo6 = { o: string | number | boolean } extends any | ||
? { o: string | number | boolean } | ||
: never | ||
|
||
type Foo7 = { o: string | number | boolean } | ||
|
||
/** | ||
* Shortly speaking: | ||
* if T is union - naked type distributes union to subtypes and maps over each of them separetely | ||
* (Transform<A | B> => Transform<A> | Transform<B> ) | ||
* if T is union - not-naked type run the conditional type for the whole union at once (?) | ||
(Transform<A | B> => Transform<A | B>) | ||
* */ |
27 changes: 27 additions & 0 deletions
27
src/ts/ts-interesting-behavior/tsDoesntKnowWhenFunctionWillBeCalled.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// declare let orderStates: OrderStates // того же поведения (1) вне фукнкции можно добиться declare - тайпскрипт не подсматривал за инициализацией ) | ||
|
||
interface OrderCreateState { | ||
status: 'create' | ||
} | ||
interface OrderCanselState { | ||
status: 'cancel' | ||
message: string | ||
} | ||
type OrderStates = OrderCreateState | OrderCanselState | ||
|
||
const orderStates: OrderStates = { | ||
status: 'create', | ||
} | ||
|
||
orderStates.status = 'cancel' // ошибка, т.к. orderStates = OrderCreateState | ||
|
||
// если так сделать, то все ок | ||
// orderStates = { | ||
// status: 'cancel', | ||
// message: 'error' | ||
// } | ||
|
||
function cancelOrder() { | ||
// тайпскрипт не может строго доказать что это присваивание приведет объёкт в неконсистентное состояниек (1) | ||
orderStates.status = 'cancel' // нет ошибки | ||
} |