Типографское безопасное обновление свойства по строковому имени свойства - PullRequest
0 голосов
/ 09 мая 2020

Мне нужно обновить значение свойства класса с помощью строкового имени свойства. Я начал с того, что убедился, что имя свойства было действительным, с помощью этого метода:

export class ClientDTO {
    ...

    static isValidPropertyName(name: string): name is keyof ClientDTO {
        return (name as keyof ClientDTO) !== undefined
    }
}

А затем в другом классе я делаю это:

foo(key: string, newValue: string) {
  if (!ClientDTO.isValidPropertyName(key)) {
    return
  }

  if (newValue !== this.originalClient[key]) {
    // @ts-ignore
    this.originalClient[key] = newValue
  }
}

Теперь поиск работает хорошо, но чтобы выполнить обновление, мне нужно поместить туда // @ts-ignore, и мне бы очень хотелось выяснить, как это сделать правильно без необходимости игнорирования.

У меня включены строгие проверки поэтому я получаю сообщение об ошибке

TS2322: Тип 'any' не может быть назначен типу 'never'

Ответы [ 2 ]

1 голос
/ 09 мая 2020

Проблема в том, что ваша защита настраиваемого типа:

isValidPropertyName(name: string): name is keyof ClientDTO { ... }

защищает от любого ключа ClientDTO, поэтому, когда вы пытаетесь его использовать:

this.originalClient[key] = newValue // newValue is type string

TypeScript - это пытается определить правильный тип для значения this.originalClient[key]. Поскольку key может быть любым ключом из ClientDTO, значение, которое вы ему присваиваете, должно быть присвоено всем типам значений этих ключей. Поскольку у вас есть несколько типов значений для этих ключей, единственный тип, который можно назначить всем из них, - это нижний тип never; тип, которому ничего не может быть присвоено, отсюда и ошибка.

Чтобы исправить это, обратите внимание, что вы указываете newValue type string. Так что ограничьте защиту типов только теми ключами из ClientDTO, значения которых являются строками:

type KeysWithStringValues<T extends {}> = {
    [K in keyof T]: T[K] extends string ? K : never;
}[keyof T];

class ClientDTO {
    /* ... */
    static isValidPropertyName(name: string): name is KeysWithStringValues<ClientDTO> {
        // Make sure to replace this with code that ACTUALLY enforces
        // the above constraint.
        return name !== undefined
    }
}

Playground Link.

1 голос
/ 09 мая 2020
return (name as keyof ClientDTO) !== undefined

Это не проверяет, что name является ключом ClientDTO. Он утверждает, что это так, а затем проверяет, не определена ли строка. Попробуйте это на игровой площадке .

Даже если бы это сработало, оно только проверило бы, является ли строка действительным ключом ClientDTO, но не скажет, какой именно. Следовательно, Typescript проверяет, что тип, который вы устанавливаете, можно безопасно назначить любому ключу ClientDTO; поскольку ClientDTO содержит «смесь типов», включая «String, Boolean, date и number», единственное безопасное значение для назначения - never.

Чтобы безопасно назначить newValue: string, вы нужна функция, которая во время выполнения гарантирует, что ваш key предназначен для свойства string, что может повлечь за собой некоторое дублирование.

class MyClass {
    constructor(
        public a: string,
        public b: string,
        public c: string,
        public x: number,
        public y: number,
        public z: number) { }
}

function isStringProperty(propertyName: string): propertyName is "a" | "b" | "c" {
    return ["a", "b", "c"].indexOf(propertyName) >= 0;
}

function isNumberProperty(propertyName: string): propertyName is "x" | "y" | "z" {
    return ["x", "y", "z"].indexOf(propertyName) >= 0;
}

function setString(dto: MyClass, key: string, newValue: string) {
    if (isStringProperty(key)) {
        dto[key] = newValue;
    }
}

function setNumber(dto: MyClass, key: string, newValue: number) {
    if (isNumberProperty(key)) {
        dto[key] = newValue;
    }
}

игровая площадка машинописного текста

См. Также: Ошибка машинописного текста : Тип 'number' нельзя присвоить типу 'never'

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...