Definindo o valor padrão para o objeto TypeScript passado como argumento


195
function sayName(params: {firstName: string; lastName?: string}) {
    params.lastName = params.lastName || 'smith';  // <<-- any better alternative to this?
    var name = params.firstName + params.lastName
    alert(name);
}

sayName({firstName: 'bob'});

Eu imaginei que algo assim pudesse funcionar:

function sayName(params: {firstName: string; lastName: string = 'smith'}) {

Obviamente, se esses argumentos eram claros, você poderia fazê-lo com:

function sayName(firstName: string, lastName = 'smith') {
    var name = firstName + lastName;
    alert(name);
}

sayName('bob');

E no coffeescript você tem acesso ao operador de existência condicional, o que pode fazer:

param.lastName ?= 'smith'

Que é compilado no javascript:

if (param.lastName == null) {
    param.lastName = 'smith';
}

Sua solução sugerida params.lastName = params.lastName || 'smith';é bastante boa - ele lida com cadeias vazias, cadeias indefinidas e valores nulos.
Fenton

@ SteveFenton E qualquer coisa falsa. É bom no caso de um sobrenome, mas geralmente não é uma boa ideia. É por isso que o Typecript converte valores padrão em if(typeof x === "undefined") { … }. Não que você não saiba disso, mas apenas apontando o caso geral do OP.
Ingo Bürk

1
@ IngoBürk é verdade, mas devido a lastName?: stringisso só pode ser como SteveFenton disse "lida com cadeias vazias, cadeias indefinidas e valores nulos".
AJP

@AJP No texto datilografado, sim. Mas isso é apenas tempo de compilação. De qualquer forma, como eu disse, apenas apontando um cenário geral :)
Ingo Bürk

2
Concordo com Steve aqui. Geralmente, é melhor ter um único argumento de configuração em vez de, por exemplo, 10 argumentos. Neste caso params.lastName = params.lastName || 'smith';é o padrão que eu uso
basarat

Respostas:


210

Na verdade, parece haver agora uma maneira simples. O código a seguir funciona no TypeScript 1.5:

function sayName({ first, last = 'Smith' }: {first: string; last?: string }): void {
  const name = first + ' ' + last;
  console.log(name);
}

sayName({ first: 'Bob' });

O truque é colocar entre parênteses as chaves que você deseja selecionar no objeto de argumento, key=valuepara quaisquer padrões. Siga isso com a :declaração de tipo e.

Isso é um pouco diferente do que você estava tentando fazer, porque em vez de ter um paramsobjeto intacto , você tem variáveis ​​dereferenciadas.

Se você deseja tornar opcional passar algo para a função, adicione a ?para todas as chaves no tipo e adicione um padrão ={}após a declaração do tipo:

function sayName({first='Bob',last='Smith'}: {first?: string; last?: string}={}){
    var name = first + " " + last;
    alert(name);
}

sayName();

Essa solução cria um recurso interessante de dica de código / intellisense, pois mostra os parâmetros totalmente destruídos. Eu não esperaria ver esse estilo em um objeto de configurações com mais de alguns parâmetros, como no exemplo.
WiredPrairie

3
Também é possível reescrever esta solução para usar uma interface / classe como "sayNameSettings"?
23417 StefhanyRath

4
Enquanto isso funciona, a destruição dos argumentos dessa maneira torna difícil seguir a IMO.
PW Kad

1
você pode usar a palavra-chave 'as' (na minha opinião, é mais legível), como {first = 'Bob', last = 'Smith'} como {first ?: string; última ?: string} mas o ideal é que você deve criar uma interface para lidar com este caso ...
Frohlich

1
Existe uma maneira de passar o objeto de opções para outra função?
Dylanthepiguy 24/03

50

O TypeScript agora suporta parâmetros padrão:

https://www.typescriptlang.org/docs/handbook/functions.html

Além disso, adicionar um valor padrão permite omitir a declaração de tipo, porque ela pode ser deduzida do valor padrão:

function sayName(firstName: string, lastName = "Smith") {
  const name = firstName + ' ' + lastName;
  alert(name);
}

sayName('Bob');

21
Como isso se aplica à pergunta do OP em relação ao valor padrão para o objeto TypeScript passado como argumento? Você deve mostrar um exemplo com um argumento de objeto.
demisx

9
Não, mas encontrei essa pergunta quando estava procurando como fornecer parâmetros padrão em texto datilografado em geral, e nenhuma das respostas abordou isso. Também postei essa resposta antes de perceber o sutil esclarecimento dos parâmetros do "objeto".
criptografa

27

A destruição de objetos no objeto de parâmetro é o que muitas das respostas acima estão buscando e o Typescript agora possui os métodos para facilitar a leitura e a compreensão intuitiva.

Noções básicas de reestruturação: Ao destruir um objeto, você pode escolher propriedades de um objeto pelo nome da chave. Você pode definir poucas ou quantas propriedades desejar e os valores padrão são definidos por uma sintaxe básica de let {key = default} = object.

let {firstName, lastName = 'Smith'} = myParamsObject;

//Compiles to:
var firstName = myParamsObject.firstName, 
_a = myParamsObject.lastName, 
lastName = _a === void 0 ? 'Smith' : _a;

Escrever uma interface, tipo ou classe para o objeto de parâmetro melhora a legibilidade.

type FullName = {
  firstName: string;
   
  /** @default 'Smith' */
  lastName ? : string;
}

function sayName(params: FullName) {

  // Set defaults for parameter object
  var { firstName, lastName = 'Smith'} = params;

  // Do Stuff
  var name = firstName + " " + lastName;
  alert(name);
}

// Use it
sayName({
  firstName: 'Bob'
});


3
Eu acho que essa é a melhor resposta para a pergunta!
Gezim 18/08/19

24

Não, o TypeScript não possui uma maneira natural de definir padrões para propriedades de um objeto definido como aquele em que um tem um padrão e o outro não. Você pode definir uma estrutura mais rica:

class Name {
    constructor(public first : string, 
        public last: string = "Smith") {

    }
}

E use isso no lugar da definição de tipo embutido.

function sayName(name: Name) {
    alert(name.first + " " + name.last);
}

Infelizmente, você não pode fazer algo assim:

function sayName(name : { first: string; last?:string } 
       /* and then assign a default object matching the signature */  
       = { first: null, last: 'Smith' }) {

} 

Como ele só definiria o padrão se namefosse undefined.


Penso que esta é a melhor resposta para esta pergunta e deve ser marcada como a resposta correta.
kapad

16

Essa pode ser uma boa maneira de fazer isso que não envolve construtores longos

class Person {
    firstName?: string = 'Bob';
    lastName?: string = 'Smith';

    // Pass in this class as the required params
    constructor(params: Person) {
        // object.assign will overwrite defaults if params exist
        Object.assign(this, params)
    }
}

// you can still use the typing 
function sayName(params: Person){ 
    let name = params.firstName + params.lastName
    alert(name)
}

// you do have to call new but for my use case this felt better
sayName(new Person({firstName: 'Gordon'}))
sayName(new Person({lastName: 'Thomas'}))

2
@PWKad os padrões são definidos na segunda e terceira linha, "Bob" e "Smith"
Cameron

3

Aqui está algo para tentar, usando a interface e destruindo com valores padrão. Observe que "lastName" é opcional.

interface IName {
  firstName: string
  lastName?: string
}

function sayName(params: IName) {
  const { firstName, lastName = "Smith" } = params
  const fullName = `${firstName} ${lastName}`

  console.log("FullName-> ", fullName)
}

sayName({ firstName: "Bob" })

0

Sem desestruturar, você pode criar um parâmetro padrão e passá-lo para

interface Name {
   firstName: string;
   lastName: string;
}

export const defaultName extends Omit<Name, 'firstName'> {
    lastName: 'Smith'
}

sayName({ ...defaultName, firstName: 'Bob' })
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.