首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Typescript 'Conditional‘类型

Typescript 'Conditional‘类型
EN

Stack Overflow用户
提问于 2018-12-02 16:01:23
回答 1查看 452关注 0票数 1

我有一个简单的函数,大体上工作很简单,但几乎每次我操作数据时,它的类型描述都很糟糕,需要类型断言。

函数:

代码语言:javascript
运行
复制
const fetch_and_encode = <T, E extends Encoded, C>({ source, encode, context }: {
    source: Fetcher<T | E> | T | E,
    encode?: Encoder<T, E, C>,
    context?: C
}): E => {
    let decoded;
    if ( typeof source === 'function' ) {
        decoded = (source as Fetcher<T | E>)();
    } else {
        decoded = source;
    }
    if ( typeof encode === 'function' ) {
        return encode(decoded as T, context);
    } else {
        return decoded as E;
    }
};

引用的类型:

代码语言:javascript
运行
复制
export type Encoded = number | string | ArrayBuffer // | Array<Encoded> | Map<string, Encoded>
export type Fetcher<T> = () => T;
export type Encoder<T, E extends Encoded, C> = (decoded: T, context?: C) => E;

它基本上有两个变量,sourceencode,每个变量可以有两种有效类型中的一种,从而导致4种状态。source可以是基准面,也可以是检索基准面的函数。encode可以是undefined,也可以是转换source的“结果”的函数。最后,这种组合必须产生一个(相对)简单类型的值,即Encoded

我已经尝试了几种不同的方法来改进类型定义,但似乎无法避免需要类型断言。这些尝试也是为了加深我对类型系统的理解,同时也是为了清理实际的定义。也就是说,“感觉”我应该能够足够严密地指定类型,以避免类型断言,我想知道是如何做到的。

我的第一次尝试,使用联合,似乎并没有真正改善类型定义:

代码语言:javascript
运行
复制
const fetch_and_encode = <T, E extends Encoded, C>( {source, encode, context}: {
    source: Fetcher<T>;
    encode: Encoder<T, E, C>;
    context?: C;
} | {
    source: Exclude<T, Function>; // T could still be a subtype of Function
    encode: Encoder<T, E, C>;
    context?: C;
} | {
    source: Fetcher<E>;
    encode: undefined;
    context?: any;
} | {
    source: E;
    encode: undefined;
    context?: any;
}): E => {
    let decoded;
    if ( typeof source === 'function' ) {
        decoded = (source as Fetcher<T | E>)();
        // decoded = source(); // Cannot invoke an expression whose type lacks a call signature.  Type 'Fetcher<T> |
        //                     // Fetcher<E> | (Exclude<T, Function> & Function)' has no compatible call signatures.
    } else {
        decoded = source;
    }
    if ( typeof encode === 'function' ) {
        return encode(decoded as T, context);
        // return encode(decoded, context); // Argument of type 'T | E' is not assignable to parameter of type 'T'
    } else {
        return decoded as E;
        // return decoded; // Type 'T | E' is not assignable to type 'E'
    }
};

然后我尝试使用实际的条件类型,但同样一无所获:

代码语言:javascript
运行
复制
const fetch_and_encode = <T, E extends Encoded, C>({ source, encode, context }: {
    source: Fetcher<T | E> | T | E,
    encode: Encoder<T, E, C> | undefined,
    context: C | undefined
} extends { source: infer S, encode: infer N, context?: C }
    ? S extends Function // Ensure S isn't a Function if it also isn't a Fetcher
        ? S extends Fetcher<T | E>
            ? N extends undefined
                ? { source: Fetcher<E>; encode: undefined; context?: any; }
                : { source: Fetcher<T>; encode: Encoder<T, E, C>; context?: C; }
            : never
        : N extends undefined
            ? { source: E; encode: undefined; context?: any; }
            : { source: T; encode: Encoder<T, E, C>; context?: C; }
    : never
): E => {
    let decoded;
    if ( typeof source === 'function' ) {
        decoded = (source as Fetcher<T | E>)();
        // decoded = source(); // Cannot invoke an expression whose type lacks a call signature.  Type 'Fetcher<T> |
        //                     // Fetcher<E> | (T & Function)' has no compatible call signatures.
    } else {
        decoded = source;
    }
    if ( typeof encode === 'function' ) {
        return encode(decoded as T, context);
        // return encode(decoded, context); // Argument of type 'T | E' is not assignable to parameter of type 'T'
    } else {
        return decoded as E;
        // return decoded; // Type 'T | E' is not assignable to type 'E'
    }
};

我不知道接下来还能去哪里。

根据suggestion of Ingo Bürk (如下),我尝试了重载,他们解决了原始问题,但提出了一个新的问题,让我困惑:

代码语言:javascript
运行
复制
function fetch_and_encode<T, E extends Encoded, C>({ source, encode, context }: {
    //   ^^^^^^^^^^^^^^^^ Overload signature is not compatible with function implementation
    source: E;
    encode: undefined;
    context?: any;
}): E;
function fetch_and_encode<T, E extends Encoded, C>({ source, encode, context }: {
    source: Fetcher<E>;
    encode: undefined;
    context?: any;
}): E;
function fetch_and_encode<T, E extends Encoded, C>({ source, encode, context }: {
    source: Fetcher<T>;
    encode: Encoder<T, E, C>;
    context?: C;
}): E;
function fetch_and_encode<T, E extends Encoded, C>({ source, encode, context }: {
    source: Exclude<T, Function>; // T could still be a subtype of Function
    encode: Encoder<T, E, C>;
    context?: C;
}): E {
    let decoded;
    if ( typeof source === 'function' ) {
        decoded = source();
    } else {
        decoded = source;
    }
    if ( typeof encode === 'function' ) {
        return encode(decoded, context);
    } else {
        return decoded;
    }
}

如果我将当前(泛型)定义添加为缺省定义,上述错误就会消失,但再次需要类型断言。

EN

回答 1

Stack Overflow用户

发布于 2018-12-04 16:11:33

下面是如何使用重载来完成此操作。注意,实际的函数体是无类型的,我找不到一个好的方法来让它工作(甚至不确定它是否可能)。但是函数调用是正确键入的。

代码语言:javascript
运行
复制
function isFetcher<T>(obj: T | Fetcher<T>): obj is Fetcher<T> {
  return typeof obj === "function";
}

function fetchAndEncode<A extends Encoded>(source: A | Fetcher<A>): A;
function fetchAndEncode<A, B extends Encoded, C>(source: A | Fetcher<A>, encode: Encoder<A, B, C>, context?: C): B;
function fetchAndEncode(source: any, encode?: any, context?: any) {
  const datum = isFetcher(source) ? source() : source;
  return encode ? encode(datum, context) : datum;
}

这将通过以下类型测试:

代码语言:javascript
运行
复制
let numericValue: number;

fetchAndEncode(numericValue); // number
fetchAndEncode(true); // error
fetchAndEncode(numericValue, val => `Hello ${val}`); // string
fetchAndEncode(() => numericValue); // number
fetchAndEncode(() => true); // error
fetchAndEncode(() => numericValue, val => `Hello ${val}`); // string
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53578459

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档