Я ищу гуру машинописи, чтобы помочь мне с этим.Я пытаюсь реализовать некоторое монадическое поведение в Typescript.У меня есть тип данных, который может принимать 4 различных формы.Я хочу, чтобы у меня была базовая монадическая функция, такая как map
и flatMap
.
. Я изо всех сил старался, чтобы компилятор машинописного текста радовался моим типам, и я пробовал несколько способов (с классами, абстрактнымиклассы, простые типы и т.д ...) но это лучшее, что я придумал.
export namespace Data {
export type Initial<T,E> = { kind: 'initial' }
export type Loading<T,E> = { kind: 'loading' }
export type Loaded<T,E> = { kind: 'loaded'; value: T }
export type Failed<T, E> = { kind: 'failed'; error?: E }
export type Data<T,E = any> = Failed<T,E> | Loaded<T,E> | Loading<T,E> | Initial<T,E>
type Kind = Data<any,any>['kind']
// Instantiations
export function loadingOf<T=any,E=any>():Loading<T,E> { return {kind: 'loading'}}
export function initialOf<T=any,E=any>():Initial<T,E> { return {kind: 'initial'}}
export function loadedOf<T=any,E=any>(value: T):Loaded<T,E> { return {kind: 'loaded', value}}
export function failedOf<T=any,E=any>(error?: E):Failed<T,E> { return {kind: 'failed', error}}
// Type guards
export function isFailed<T,E>( data: Data<T,E>): data is Failed<T,E> { return data.kind === 'failed'}
export function isLoaded<T,E>( data: Data<T,E>): data is Loaded<T,E> { return data.kind === 'loaded'}
export function isInitial<T,E>( data: Data<T,E>): data is Initial<T,E> { return data.kind === 'initial'}
export function isLoading<T,E>( data: Data<T,E>): data is Loading<T,E> { return data.kind === 'loading'}
// Map
export function map<T,U,E>(fn: (t: T) => U, data: Loaded<T,E>): Loaded<U,E>;
export function map<T,U,E>(fn: (t: T) => U, data: Failed<T,E>): Failed<U,E>;
export function map<T,U,E>(fn: (t: T) => U, data: Loading<T,E>): Loading<U,E>;
export function map<T,U,E>(fn: (t: T) => U, data: Initial<T,E>): Initial<U,E>;
export function map<T,U,E>(fn: (t: T) => U, data: Data<T,E>) {
if(isLoaded(data)) {
return loadedOf(fn(data.value))
}
return data
}
}
const a = Data.map(x => x, Data.loadedOf('hi'))
const b = Data.map(x => x, Data.loadingOf<string>())
const c = Data.map(x => x, Data.initialOf<string>())
const d = Data.map(x => x, Data.failedOf<string>())
const datas:Data.Data<string>[] = [a,b,c,d]
datas.map(data => Data.map(x=>x, data))
Это, конечно, неполно, потому что машинопись не в восторге от функции карты.
При проверке типа a
, b
, c
, d
это нормально.Но при попытке сделать это над массивом выдает такую ошибку:
Argument of type 'Data<string, any>' is not assignable to parameter of type 'Initial<string, any>'.
Type 'Loaded<string, any>' is not assignable to type 'Initial<string, any>'.
Types of property 'kind' are incompatible.
Type '"loaded"' is not assignable to type '"initial"'.ts(2345)
Если я поменяю местами порядок перегрузок, он просто изменит последнюю строку кода ошибки с initial
на что-то другое.
Я не понимаю, почему он недоволен типами.Точно не подходит Data<T,E>
?Почему он пытается подобрать тип аргумента, чтобы он соответствовал всем типам аргументов возможных перегрузок?
IF Я удаляю подписи с подтипами и сохраняю только самый общий, после чего перестает жаловаться НО это сделает типы свободными.Любой подтип будет приведен к более свободному типу Data<T,E>
, что мне не нужно.