代码之家  ›  专栏  ›  技术社区  ›  Max Heiber

有没有一种方法可以从TypeScript中的类型谓词中获取有用的类型知识?

  •  0
  • Max Heiber  · 技术社区  · 7 年前

    验证数据时,有时需要: -数据是否有效 -如果不是,则验证错误

    我们可以在TypeScript中以一种自然的JS-y方式来实现这一点吗?像下面这样的工作吗?

    
    type Person = {
        name: string,
        height: number,
    };
    
    function validatePerson(obj: object): obj is Person & string[] {
    
        const missingKeys = ["dependencies", "name"]
            .filter(key => key in obj)
            .map(key => `key ${key} is missing from obj`);
    
        if (!missingKeys.length) {
            return missingKeys as typeof missingKeys & true;
        }
        else {
            return missingKeys as typeof missingKeys & false;
        }
    }
    
    const x = { name: "ff", height: getHeight()};
    const validationErrors = validatePerson(x);
    if (!validationErrors.length) {
        const b: Person = x;
    }
    
    function getHeight(): any {
        return 40;
    }
    

    master :

    type Person = {
        name: string,
        height: number,
    };
    
    function validatePerson(obj: object, placeToPushValidationErrors: string[]): obj is Person {
    
        const missingKeys = ["dependencies", "name"]
            .filter(key => key in obj)
            .map(key => `key ${key} is missing from obj`);
    
        placeToPushValidationErrors.push(...missingKeys);
    
        return !missingKeys.length;
    }
    
    // imagine this is coming from untrustworthy JSON
    function getHeight(): unknown {
        return 40;
    }
    
    const person = { name: "ff", height: getHeight() };
    const validationErrors = [];
    if (validatePerson(person, validationErrors)) {
        const p: Person = person;
    }
    else {
        throw Error(`invalid person: ${validationErrors.join(', ')}`)
    }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   jcalz    7 年前

    不幸的是,您不能真正为此使用类型谓词。它们在并集、交点或对象特性类型中不可用。它们唯一能真正出现的地方是作为函数的返回类型,并且它们与传递到函数中的一个参数绑定在一起。和类型谓词与 boolean 在某种程度上,您不希望出现在这里,因为您希望返回值类型是数组,但 true false 不是数组。

    选择性地 Person . 如果

    function validatePerson(obj: object) {
    
      const validationErrors = ["dependencies", "name"]
        .filter(key => key in obj)
        .map(key => `key ${key} is missing from obj`);
    
      const ret: { person?: Person, validationErrors: string[] } = { validationErrors };
    
      if (validationErrors.length === 0) {
        ret.person = obj as Person;
      }
      return ret;
    
    }
    
    declare const x: object;
    const validatedPerson = validatePerson(x);
    
    if (validatedPerson.person) {
      const b: Person = validatedPerson.person
    } else {
      throw Error(`invalid person: ${validatedPerson.validationErrors.join(', ')}`)
    }
    

    validatedPerson.person 作为一种类型保护,它会自动缩小。


    一个更简洁的解决方案是按照@TitianCernicova Dragomir的建议,返回

    function validatePerson(obj: object) {
      const validationErrors = ["dependencies", "name"]
        .filter(key => key in obj)
        .map(key => `key ${key} is missing from obj`);
      return (validationErrors.length) ? validationErrors : (obj as Person);
    }
    
    declare const x: object;
    const validatedPerson = validatePerson(x);
    
    if (!Array.isArray(validatedPerson)) {
      const b: Person = validatedPerson;
    } else {
      throw Error(`invalid person: ${validatedPerson.join(', ')}`)
    }
    

    现在,返回类型为 validatePerson() Person | string[] . 然后使用返回类型是否为数组作为类型保护,这一切都可以正常工作。


    希望这些能给你一些前进的想法。祝你好运