【问题标题】:TypeScript - How do you chain accessing optional nested type properties?TypeScript - 你如何链接访问可选的嵌套类型属性?
【发布时间】:2021-08-06 00:34:05
【问题描述】:

我有一个Client 类,用于存储应用程序需要保存在内存中的其他对象的缓存。对象缓存的结构是开发人员定义的。例如,如果我们有 Example 对象的缓存:

class Example {
    property1: string;
    property2: string;
}

开发者可能只想缓存property1


import { EventEmitter } from "events";

// Constructor options for `Client`
interface ClientData {
    cacheStrategies?: CacheStrategies;
}

// How various objects should be cached
interface CacheStrategies {
    example?: ExampleCacheStrategies;
    ...
}

// Metadata for how each type of object should be cached
interface ExampleCacheStrategies {
    cacheFor?: number;
    customCache?: ExampleCustomCacheData;
}

// The custom structure of what parts of `example` objects should be cached
interface ExampleCustomCacheData {
    property1?: boolean;
    property2?: boolean;
}

// The object stored in `Client.exampleCache`, based on the custom structure defined in `ExampleCustomCacheData`
interface CustomExampleData<CachedExampleProperties extends ExampleCustomCacheData> {
    property1: CachedExampleProperties["property1"] extends true ? string /* 1 */ : undefined;
    property2: CachedExampleProperties["property2"] extends true ? string : undefined;
}

class Client<ClientOptions extends ClientData> extends EventEmitter {

    // The map's value should be based on the custom structure defined in `ExampleCustomCacheData`
    exampleCache: Map<string, CustomExampleData<ClientOptions["cacheStrategies"]["example"]["customCache"]>>;

    constructor(clientData: ClientOptions) {
        super();
        this.exampleCache = new Map();
    }
}

const client = new Client({
    cacheStrategies: {
        example: {

            /**
             * The properties of `example` objects that should be cached
             * This says that `property1` should be cached (string (1))
             */
            customCache: {
                property1: true, // (2)
                ... // (3)
            }
        }
    }
});

client.exampleCache.set("123", {
    property1: "value"
});

const exampleObject = client.exampleCache.get("123");

if (exampleObject) {

    // Should be `string` instead of `string | undefined` (2)
    console.log(exampleObject.property1);

    // `string | undefined`, as expected since it's falsey (3)
    console.log(exampleObject.property2);
}

正如console.log()s 上方的 cmets 中所解释的,目标是从缓存中拉出的对象使 property1 成为 string 而不是 string | undefined

问题是exampleCache: Map&lt;string, CustomExampleData&lt;ClientOptions["cacheStrategies"]["example"]["customCache"]&gt;&gt;; 不起作用,因为ClientOptions["cacheStrategies"]ClientOptions["cacheStrategies"]["example"] 都是可选的。以下也不起作用:

exampleCache: Map<string, CustomExampleData<ClientOptions["cacheStrategies"]?.["example"]?.["customCache"]>>;

'&gt;' expected?. 出现错误。我该如何解决这个问题?

【问题讨论】:

  • 这里的"bans" 是什么?请考虑修改此问题中的代码以构成minimal reproducible example,将其放入像The TypeScript Playground (link) 这样的独立IDE 时,清楚地展示了您面临的问题(并且只有那个问题)。这将使那些想要帮助您的人立即着手解决问题,而无需首先重新创建它。它将使您得到的任何答案都可以针对定义明确的用例进行测试。
  • @jcalz 我的错,发帖前忘记改了

标签: javascript typescript types type-parameter


【解决方案1】:

the optional chaining operator ?.non-null assertion operator ! 这样的语法只适用于值表达式,它会以某种形式传递给JavaScript。但是你需要一些与类型表达式一起工作的东西,它只存在于静态类型系统中并且在转译时是erased

有一个NonNullable&lt;T&gt; utility type,它是非空断言运算符的类型系统模拟。给定一个union 类型TNonNullable&lt;T&gt; 类型将与T 相同,但没有任何nullundefined 类型的联合成员:

type Foo = string | number | undefined;
type NonNullableFoo = NonNullable<Foo>;
// type NonNullableFoo = string | number

事实上,编译器实际上使用它来表示应用了非空断言运算符的表达式的类型:

function nonNullAssertion<T>(x: T) {
    const nonNullX = x!;
    // const nonNullX: NonNullable<T>
}

所以,无论你有一个类型 T,其中包括 nullundefined,并且你想删除它,你都可以使用 NonNullable&lt;T&gt;。在您的代码中,您将需要多次执行此操作。为了简洁(代码,不是我的解释),让我们使用更短的别名:

type NN<T> = NonNullable<T>;

然后

class Client<C extends ClientData> extends EventEmitter {

  exampleCache: Map<string, CustomExampleData<
      NN<NN<NN<C["cacheStrategies"]>["example"]>["customCache"]>>
  >;

}

这编译没有错误,并且表现出我认为你想要的样子:

console.log(exampleObject.property1.toUpperCase()); // string
console.log(exampleObject.property2); // undefined

Playground link to code

【讨论】:

    猜你喜欢
    • 2022-01-22
    • 1970-01-01
    • 2019-03-12
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    • 2018-06-03
    • 2020-09-14
    • 1970-01-01
    相关资源
    最近更新 更多